13. 练习 [IMPOTS] 配合服务 WEB
我们回到练习题 [IMPOTS],该题已在第 4.2、4.3、8 和 10 节中讨论过。
![]() |
13.1. 服务器
我们处于以下客户端-服务器架构中:
![]() |
在服务器端,我们将再次采用三层架构。[dao]层将由ImpotsMySQL类实现,该类已在第10节中与SGBD和MySQL一起使用。 [metier]层将由之前已探讨过的[ImpotsMetier]类实现。 因此,剩下的工作就是编写Web服务。该服务从客户端接收一个名为params的参数,格式为params= yes,2,200000,其中第一个元素表示纳税人是否已婚,第二个表示子女数量,第三个表示年薪。
Web服务代码如下(impots_web_01):
#!D:\Programs\ActivePython\Python2.7.2\python.exe
# -*- coding=Utf-8 -*-
# 导入 Impots* 类模块
from impots import *
import cgi,cgitb,re
# 允许显示调试信息
cgitb.enable()
sys.stderr=sys.stdout
# ------------------------------------------------
# 税务Web服务
# ------------------------------------------------
# 计算税款所需的数据已放入表 mysqL TABLE
# 该表属于数据库 BASE。该表的结构如下
# 限制 decimal(10,2),coeffR decimal(6,2),coeffN decimal(10,2)
# 纳税人的参数(婚姻状况、子女数量、年薪)
# 由客户端以 params=婚姻状况, 子女数量, 年薪 的形式发送
# 结果(婚姻状况、子女数量、年薪、应缴税款)将返回给客户端
# 以 <税款>数值</税款> 的形式返回
# 若参数无效,则以 <错误>msg</错误> 的形式返回
# 服务器向客户端返回未格式化的文本
print "Content-Type: text/plain\n"
# 常量定义
USER="root"
PWD=""
HOTE="localhost"
BASE="dbimpots"
TABLE="impots"
# 创建 [metier] 层
# Python似乎不支持会话
# 在 PHP 中,本应使用会话来存储业务对象
# 在此处,我们系统地根据数据库数据构建它MySQL
# QZXW2HTML层的实例化 BW21ldGllcl0ZQX
try:
metier=ImpotsMetier(ImpotsMySQL(HOTE,USER,PWD,BASE,TABLE))
except (IOError, ImpotsError) as infos:
print ("<erreur>Une erreur s'est produite : {0}</erreur>".format(infos))
sys.exit()
# 获取客户端发送给服务器的数据行
params=cgi.FieldStorage().getlist('params')
# 若无参数则报错
if not params:
print "<erreur>Le parametre [params] est absent<erreur>"
sys.exit()
# 处理参数 params
items=params[0].strip().lower().split(',')
# 参数字段必须仅包含3个
if len(items)!=3:
print "<erreur>[%s] : nombre de parametres invalide</erreur>" % (params[0])
sys.exit()
# 第一个参数(婚姻状况)必须为是/否
marie=items[0].strip()
if marie!="oui" and marie != "non":
print "<erreur>[%s] : 1er parametre invalide</erreur>\n"% (params[0])
sys.exit()
# 第二个参数(子女数)必须为整数
match=re.match(r"^\s*(\d+)\s*$",items[1])
if not match:
print "<erreur>[%s] : 2ieme parametre invalide</erreur>\n"% (params[0])
sys.exit()
enfants=int(match.groups()[0])
# 第三个参数(工资)必须为整数
match=re.match(r"^\s*(\d+)\s*$",items[2])
if not match:
print "<erreur>[%s] : 3ieme parametre invalide</erreur>\n"% (params[0])
sys.exit()
salaire=int(match.groups()[0])
# 计算税额
impot=metier.calculer(marie,enfants,salaire)
# 返回结果
print "<impot>%s</impot>\n" % (impot)
# 结束
注:
- 第 5 行:导入 impots.py 文件中的对象;
- 第9行:调试CGI脚本可能会遇到问题。 模块 cgitb 在发生错误时会发送崩溃原因。为此,需将标准错误输出(sys.stderr)重定向至标准输出(sys.stdout)(第 10 行);
- 第 26 行:Http 标头用于确定发送文档的类型,此处为未格式化的文本。Web 服务将响应以单行文本的形式返回:
- 若发生错误,则返回 <erreur>message</erreur>;
- <impot>valeur</impot>,其中 valeur 为税额。
- 第 43 行:实例化 [dao] 和 [metier] 层;
- 第 44-46 行:若实例化出现错误,脚本将发送响应并终止;
- 第 49-53 行:获取 'params' 参数。若该参数不存在,脚本将返回响应并终止;
- 第 56 行:将参数 'oui,2,200000' 拆分为 items 数组中的 3 个字段;
- 第58-77行:验证这3个字段的有效性。若存在错误,脚本将返回响应并终止;
- 第79行:计算税额;
- 第81行:并将结果发送给客户端。
在网页浏览器中,将得到以下结果:
![]() | ![]() |
13.2. 一个预设的客户
该程序(client_impots_web_01)
# -*- coding=utf-8 -*-
import httplib,urllib,re
# 常量
HOST="localhost"
URL="/cgi-bin/impots_web_01b.py"
data=("oui,2,200000","non,2,200000","oui,3,200000","non,3,200000","x,y,z,t","x,2,200000", "oui,x,200000","oui,2,x");
# 连接
connexion=httplib.HTTPConnection(HOST)
# 跟踪
#connexion.set_debuglevel(1)
# 循环处理要发送给服务器的数据
for params in data:
# 参数在发送至服务器前必须进行编码
parametres = urllib.urlencode({'params': params})
# 发送请求
connexion.request("POST",URL,parametres)
# 处理响应(文本行)
reponse=connexion.getresponse().read()
lignes=reponse.split("\n")
# 文本行格式为
# <impot>xxx</impot>
# 或 <error>xxx</error>
impot=re.match(r"^<impot>(\S+)</impot>\s*$",lignes[0])
if impot:
print "params=%s, impot=%s" % (params,impot.groups()[0])
else:
erreur=re.match(r"^<erreur>(.+)</erreur>\s*$",lignes[0])
if erreur:
print "params=%s, erreur=%s" % (params,erreur.groups()[0])
else:
print lignes
# 关闭连接
connexion.close()



