Skip to content

15. 练习 [IMPOTS] 配合 XML

在这个已经多次研究过的练习中,服务器以 XML 流的形式将结果返回给客户端:

  • 若发生错误,则返回 <reponse><erreur>msg</erreur></reponse>
  • 若成功计算出税额,则返回 <reponse><impot>valeur</impot></reponse>

我们将运用刚刚学到的关于解析 XML 文档的知识。

15.1. Web服务

该 Web 服务与之前研究过的 Web 服务并无二致,只是发送给客户端的响应 XML 略有不同。架构保持不变:


Web 服务 (impots_web_02)


#!D:\Programs\ActivePython\Python2.7.2\python.exe

# -*- coding=Utf-8 -*-
# 导入 Impots* 类模块
from impots import *
import cgi,cgitb,re

# 允许显示调试信息
cgitb.enable()

# ------------------------------------------------ 
# 税务Web服务
# ------------------------------------------------ 

# 计算税款所需的数据已放入表 mysqL TABLE
# 该表属于数据库 BASE。该表的结构如下
# 限制 decimal(10,2),coeffR decimal(6,2),coeffN decimal(10,2)
# 纳税人的参数(婚姻状况、子女数量、年薪)
# 由客户端以 params=婚姻状况, 子女数量, 年薪 的形式发送
# 结果(婚姻状况、子女数量、年薪、应缴税款)将返回给客户端
# 以 <税款>数值</税款> 的形式返回
# 若参数无效,则以 <错误>msg</错误> 的形式返回

# 服务器向客户端返回未格式化的文本
print "Content-Type: text/plain\n"
# 响应开头
print "<reponse>"

# 常量定义
USER="root"
PWD=""
HOTE="localhost"
BASE="dbimpots"
TABLE="impots"

# [metier] 层的实例化
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></reponse>"
    sys.exit()

# 处理参数 params  
#print "接收到的参数 --> %s\n" %(params)
items=params[0].strip().lower().split(',')
# 参数字段应仅包含 3 个
if len(items)!=3:
    print "<erreur>[%s] : nombre de parametres invalide</erreur></reponse>" % (params[0])
    sys.exit()
# 第一个参数(婚姻状况)必须为是/否
marie=items[0].strip()
if marie!="oui" and marie != "non":
    print "<erreur>[%s] : 1er parametre invalide</erreur></reponse>\n"% (params[0])
    sys.exit()
# 第二个参数(子女数)必须是整数
match=re.match(r"^\s*(\d+)\s*$",items[1])
if not match:
    print "<erreur>[%s] : 2ieme parametre invalide</erreur></reponse>\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] : 2ieme parametre invalide</erreur></reponse>\n"% (params[0])
    sys.exit()
salaire=int(match.groups()[0])
# 计算税额
impot=metier.calculer(marie,enfants,salaire)
# 返回结果
print "<impot>%s</impot></reponse>\n" % (impot)
# 结束

该 Web 服务与前一个的区别仅在于响应的性质:

若出现错误,返回 <reponse><erreur>msg</erreur></reponse> 而非 <erreur>msg</erreur>

若成功计算税额,则返回 <reponse><impot>valeur</impot></reponse> 而非 <impot>valeur</impot>

15.2. 客户端程序

我们的客户端需要分析Web服务返回的XML响应。我们将应用之前在分析XML文档时所学的内容。


程序(client_impots_web_02)


# -*- coding=utf-8 -*-

import httplib,urllib,re
import xml.sax, xml.sax.handler
# 管理类 XML
class XmlHandler(xml.sax.handler.ContentHandler):

    # 遇到开始标签时调用的函数
    def startElement(self,name,attributs):
        # 记录当前元素
        global elementcourant
        elementcourant=name.strip().lower()

    # 遇到结束标签时调用的函数
    def endElement(self,name):
        # 不执行任何操作
        pass

    # 数据管理函数
    def characters(self,data):
        # 数据
        global elementcourant,elements

        # 数据已获取
        match=re.match(r"^\s*(.+?)\s*$",data)
        if match:
            elements[elementcourant]=match.groups()[0].lower()
    
def getResultatsXml(reponse):
    # 分析响应XML
    xml.sax.parseString(reponse,XmlHandler())
    # 返回结果
    if elements.has_key('erreur'):
        return (elements['erreur'],"")
    else:
        return ("",elements['impot'])

# ------------------------------------------------------------ main
# 常量
HOST="localhost"
URL="/cgi-bin/impots_web_02b.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");
# 全局变量
elementcourant=""
elements={}

# 连接
connexion=httplib.HTTPConnection(HOST)
# 跟踪
#connexion.set_debuglevel(1)
# 循环处理要发送至服务器的数据
for params in data:
    # 参数在发送至服务器前必须进行编码
    parametres = urllib.urlencode({'params': params})
    # 发送请求
    connexion.request("POST",URL,parametres)
    # 处理响应(流 XML)
    reponse=connexion.getresponse().read()
    # 解析 XML 文件
    (erreur,impot)=getResultatsXml(reponse)
    if not erreur:
        print "impot[%s]=%s" % (params,impot)
    else:
        print "erreur[%s]=%s" % (params,erreur)

注:

  • Web 服务的 XML 流程由函数 getResultatsXml(第 60 行)调用;
  • 第29行:函数getResultatsXml
  • 第 31 行:Web 服务的响应 XML 由第 6 行定义的 XmlHandler 类的实例进行解析;
  • 第6行:类XmlHandler实现了3个方法:startElementendElement和characters。利用这3个方法,创建了一个字典。 键是标签名称 <erreur> 和 <impot>,值是与这两个标签关联的数据;
  • 第33-36行:函数getResultatsXml返回一个包含2个元素的元组:
    • 如果对数据流 XML 的分析显示存在标签 <erreur>,则返回 (erreur,"")。此时,erreur 即代表该标签的内容;
    • ("",impot):如果对数据流 XML 的分析显示存在标签 <impot>,则 impot 即代表该标签的内容。
  • 第 60 行:获取函数 getResultatsXml 的结果,并在第 61-64 行中进行处理。

15.3. 结果

1
2
3
4
5
6
7
8
impot[oui,2,200000]=22504.0
impot[non,2,200000]=33388.0
impot[oui,3,200000]=16400.0
impot[non,3,200000]=22504.0
erreur[x,y,z,t]=[x,y,z,t] : nombre de parametres invalide
erreur[x,2,200000]=[x,2,200000] : 1er parametre invalide
erreur[oui,x,200000]=[oui,x,200000] : 2ieme parametre invalide
erreur[oui,2,x]=[oui,2,x] : 2ieme parametre invalide