15. Exercício [Cálculo de impostos] com XML
Neste exercício, que já abordámos várias vezes, o servidor devolve os resultados ao cliente sob a forma de um fluxo XML:
- <response><error>msg</error></response> em caso de erro;
- <response><tax>value</tax></response> se o imposto puder ser calculado.
Utilizamos o que acabámos de aprender sobre a análise de um documento XML.
15.1. O Serviço Web
O serviço Web não difere do que foi estudado anteriormente, exceto pelo facto de a resposta XML enviada ao cliente ser ligeiramente diferente. A arquitetura permanece a mesma:
O serviço web (impots_web_02)
| # !D:\Programs\ActivePython\Python2.7.2\python.exe
# -*- coding=Utf-8 -*-
# import of Impots* class module
from impots import *
import cgi,cgitb,re
# allow debugging information to be displayed
cgitb.enable()
# ------------------------------------------------
# tax web service
# ------------------------------------------------
# the data required to calculate the tax has been placed in table mysqL TABLE
# belonging to the BASE database. The table has the following structure
# limits decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2)
# taxable person parameters (marital status, number of children, annual salary)
# are sent by the customer in the form params=marital status, number of children, annual salary
# results (marital status, number of children, annual salary, tax payable) are returned to the customer
# in the form <impot>value</impot>
# or as <error>msg</error>, if parameters are invalid
# the server returns unformatted text to the client
print "Content-Type: text/plain\n"
# start of answer
print "<reponse>"
# definition of constants
USER="root"
PWD=""
HOTE="localhost"
BASE="dbimpots"
TABLE="impots"
# instantiation layer [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()
# retrieve the line sent by the client to the server
params=cgi.FieldStorage().getlist('params')
# if no parameters then error
if not params:
print "<erreur>Le parametre [params] est absent<erreur></reponse>"
sys.exit()
# we use the params parameter
# print "parameters received --> %s\n" %(params)
items=params[0].strip().lower().split(',')
# there must be only 3 fields
if len(items)!=3:
print "<erreur>[%s] : nombre de parametres invalide</erreur></reponse>" % (params[0])
sys.exit()
# first parameter (marital status) must be yes/no
marie=items[0].strip()
if marie!="oui" and marie != "non":
print "<erreur>[%s] : 1er parametre invalide</erreur></reponse>\n"% (params[0])
sys.exit()
# the second parameter (number of children) must be an integer
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])
# the third parameter (salary) must be an integer
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])
# tax calculation
impot=metier.calculer(marie,enfants,salaire)
# return the result
print "<impot>%s</impot></reponse>\n" % (impot)
# end
|
Notas:
Este serviço web difere do anterior apenas na natureza da sua resposta:
<response><error>msg</error></response> em caso de erro, em vez de <error>msg</error>
<response><tax>value</tax></response> se o imposto puder ser calculado, em vez de <tax>value</tax>
15.2. A aplicação cliente
O nosso cliente deve analisar a resposta XML enviada pelo serviço web. Aplicamos o que aprendemos na análise de um documento XML.
O programa (client_impots_web_02)
| # -*- coding=utf-8 -*-
import httplib,urllib,re
import xml.sax, xml.sax.handler
# management class XML
class XmlHandler(xml.sax.handler.ContentHandler):
# function called when a start tag is encountered
def startElement(self,name,attributs):
# note the current element
global elementcourant
elementcourant=name.strip().lower()
# the function called when an end tag is encountered
def endElement(self,name):
# we do nothing
pass
# the data management function
def characters(self,data):
# data
global elementcourant,elements
# data are retrieved
match=re.match(r"^\s*(.+?)\s*$",data)
if match:
elements[elementcourant]=match.groups()[0].lower()
def getResultatsXml(reponse):
# we analyze the XML response
xml.sax.parseString(reponse,XmlHandler())
# we return the results
if elements.has_key('erreur'):
return (elements['erreur'],"")
else:
return ("",elements['impot'])
# ------------------------------------------------------------ main
# constant
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");
# global variables
elementcourant=""
elements={}
# connection
connexion=httplib.HTTPConnection(HOST)
# follow-up
# connexion.set_debuglevel(1)
# loop over the data to be sent to the server
for params in data:
# parameters must be encoded before being sent to the server
parametres = urllib.urlencode({'params': params})
# send request
connexion.request("POST",URL,parametres)
# response processing (stream XML)
reponse=connexion.getresponse().read()
# xml file processing
(erreur,impot)=getResultatsXml(reponse)
if not erreur:
print "impot[%s]=%s" % (params,impot)
else:
print "erreur[%s]=%s" % (params,erreur)
|
Notas:
- O feed XML do serviço web é processado pela função getResultatsXml (linha 60);
- linha 29: a função getResultatsXml;
- linha 31: a resposta XML do serviço web é analisada por uma instância da classe XmlHandler definida na linha 6;
- linha 6: a classe XmlHandler implementa os três métodos startElement, endElement e characters. Utilizando estes três métodos, é criado um dicionário. As chaves são os nomes das tags <error> e <import>, e os valores são os dados associados a estas duas tags;
- linhas 33–36: a função getResultatsXml devolve uma tupla com dois elementos:
- (error, "") se a análise do fluxo XML detectou a tag <error>. error representa então o conteúdo desta tag;
- ("", impot) se a análise do fluxo XML revelou a presença da tag <impot>. impot representa então o conteúdo desta tag.
- Linha 60: O resultado da função getResultatsXml é recuperado e, em seguida, processado nas linhas 61–64.
15.3. Os resultados
| 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
|