Skip to content

15. Exercício [IMPOTS] com XML

Neste exercício, já estudado inúmeras vezes, o servidor devolve os resultados ao cliente sob a forma de um fluxo XML:

  • <reponse><erreur>msg</erreur></reponse> em caso de erro;
  • <reponse><impot>valeur</impot></reponse> se o imposto tiver sido 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 serviço web analisado anteriormente, exceto pelo facto de a resposta XML enviada ao cliente ser ligeiramente diferente. A arquitetura mantém-se a mesma:


O serviço web (impots_web_02)


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

# -*- coding=Utf-8 -*-
# importação do módulo das classes Impostos*
from impots import *
import cgi,cgitb,re

# autoriza-se a exibição de informações de depuração
cgitb.enable()

# ------------------------------------------------ 
# o serviço web dos impostos
# ------------------------------------------------ 

# os dados necessários para o cálculo do imposto foram colocados na tabela mysqL TABLE
# pertencente à base de dados BASE. A tabela tem a seguinte estrutura
# limites decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2)
# os parâmetros dos contribuintes (estado civil, número de filhos, salário anual)
# são enviados pelo cliente no formato params=estado civil, número de filhos, salário anual
# os resultados (estado civil, número de filhos, salário anual, imposto a pagar) são devolvidos ao cliente
# na forma <imposto>valor</imposto>
# ou no formato <erro>msg</erro>, se os parâmetros forem inválidos

# o servidor devolve ao cliente texto não formatado
print "Content-Type: text/plain\n"
# início da resposta
print "<reponse>"

# definição das constantes
USER="root"
PWD=""
HOTE="localhost"
BASE="dbimpots"
TABLE="impots"

# instanciação da camada [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()

# recupera-se a linha enviada pelo cliente ao servidor
params=cgi.FieldStorage().getlist('params')
# se não houver parâmetros, ocorre um erro
if not params:
    print "<erreur>Le parametre [params] est absent<erreur></reponse>"
    sys.exit()

# processa-se o parâmetro «params»  
#print "parâmetros recebidos --> %s\n" %(params)
items=params[0].strip().lower().split(',')
# só deve haver 3 campos
if len(items)!=3:
    print "<erreur>[%s] : nombre de parametres invalide</erreur></reponse>" % (params[0])
    sys.exit()
# o primeiro parâmetro (estado civil) deve ser sim/não
marie=items[0].strip()
if marie!="oui" and marie != "non":
    print "<erreur>[%s] : 1er parametre invalide</erreur></reponse>\n"% (params[0])
    sys.exit()
# o segundo parâmetro (número de filhos) deve ser um número inteiro
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])
# o terceiro parâmetro (salário) deve ser um número inteiro
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])
# calcula-se o imposto
impot=metier.calculer(marie,enfants,salaire)
# retorna-se o resultado
print "<impot>%s</impot></reponse>\n" % (impot)
# fim

Notas:

Este serviço web difere do anterior apenas pela natureza da sua resposta:

<reponse><erreur>msg</erreur></reponse> em caso de erro, em vez de <erreur>msg</erreur>

<reponse><impot>valeur</impot></reponse> se o imposto tiver sido calculado, em vez de <impot>valeur</impot>

15.2. O cliente programado

O nosso cliente deve analisar a resposta XML enviada pelo serviço web. Aplicamos o que foi visto 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
# classe de gestão XML
class XmlHandler(xml.sax.handler.ContentHandler):

    # função chamada ao encontrar uma baliza de início
    def startElement(self,name,attributs):
        # regista-se o elemento atual
        global elementcourant
        elementcourant=name.strip().lower()

    # a função chamada ao encontrar uma baliza de fim
    def endElement(self,name):
        # não se faz nada
        pass

    # a função de gestão de dados
    def characters(self,data):
        # dados
        global elementcourant,elements

        # os dados são recuperados
        match=re.match(r"^\s*(.+?)\s*$",data)
        if match:
            elements[elementcourant]=match.groups()[0].lower()
    
def getResultatsXml(reponse):
    # analisa-se a resposta XML
    xml.sax.parseString(reponse,XmlHandler())
    # os resultados são apresentados
    if elements.has_key('erreur'):
        return (elements['erreur'],"")
    else:
        return ("",elements['impot'])

# ------------------------------------------------------------ main
# constantes
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");
# variáveis globais
elementcourant=""
elements={}

# ligação
connexion=httplib.HTTPConnection(HOST)
# acompanhamento
#connexion.set_debuglevel(1)
# repete os dados a enviar para o servidor
for params in data:
    # os parâmetros têm de ser codificados antes de serem enviados para o servidor
    parametres = urllib.urlencode({'params': params})
    # envio do pedido
    connexion.request("POST",URL,parametres)
    # processamento da resposta (fluxo XML)
    reponse=connexion.getresponse().read()
    # análise do ficheiro XML
    (erreur,impot)=getResultatsXml(reponse)
    if not erreur:
        print "impot[%s]=%s" % (params,impot)
    else:
        print "erreur[%s]=%s" % (params,erreur)

Notas:

  • o fluxo 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. Com a ajuda destes três métodos, cria-se um dicionário. As chaves são os nomes das etiquetas <erreur> e <impot> e os valores são os dados associados a estas duas etiquetas;
  • linhas 33-36: a função getResultatsXml devolve um tuplo com 2 elementos:
    • (erreur,"") se a análise do fluxo XML tiver revelado a existência da baliza <erreur>. erreur representa, então, o conteúdo dessa baliza;
    • ("",impot) se a análise do fluxo XML tiver revelado a existência da baliza <impot>. impot representa, então, o conteúdo dessa baliza.
  • linha 60: o resultado da função getResultatsXml é recuperado e, em seguida, processado nas linhas 61-64.

15.3. Os resultados

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