Skip to content

13. Exercício [IMPOTS] com um serviço WEB

Voltamos ao exercício [IMPOTS], já abordado nos parágrafos 4.2, 4.3, 8 e 10.

13.1. O servidor

Estamos na seguinte arquitetura cliente-servidor:

No lado do servidor, utilizaremos novamente uma arquitetura de três camadas. A camada [dao] será implementada pela classe ImpotsMySQL, utilizada na versão com as classes SGBD e MySQL, conforme descrito no parágrafo 10. A camada [metier] será implementada pela classe [ImpotsMetier], já analisada. Resta, portanto, apenas escrever o serviço web. Este recebe de seus clientes um parâmetro params na forma params= sim,2,200000, em que o primeiro elemento indica se o contribuinte é casado ou não, o segundo, o número de filhos, e o terceiro, seu salário anual.

O serviço web é o seguinte (impots_web_01):


#!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()
sys.stderr=sys.stdout

# ------------------------------------------------ 
# o serviço web de impostos
# ------------------------------------------------ 

# os dados necessários para o cálculo do imposto foram inseridos na tabela mysqL TABLE
#, pertencente ao banco de dados BASE. A tabela possui 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
# no formato <imposto>valor</imposto>
# ou no formato <erro>msg</erro>, caso os parâmetros sejam inválidos

# o servidor retorna ao cliente texto não formatado
print "Content-Type: text/plain\n"

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


# cria-se a camada [metier]
# O Python parece não oferecer suporte a sessões
# no PHP, teríamos usado uma sessão para armazenar o objeto de negócio
# aqui, ele é construído sistematicamente a partir dos dados do banco de dados MySQL

# 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()

# recuperamos 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>"
  sys.exit()

# processamos o parâmetro params  
items=params[0].strip().lower().split(',')
# deve haver apenas 3 campos
if len(items)!=3:
  print "<erreur>[%s] : nombre de parametres invalide</erreur>" % (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>\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>\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] : 3ieme parametre invalide</erreur>\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>\n" % (impot)
# fim

Notas:

  • linha 5: importam-se os objetos do arquivo impots.py;
  • linha 9: depurar um script CGI pode ser problemático. O módulo cgitb envia, em caso de erro, o motivo da falha. Para isso, é necessário redirecionar a saída padrão de erros (sys.stderr) para a saída padrão (sys.stdout) (linha 10);
  • linha 26: o cabeçalho HTTP que define a natureza do documento enviado, neste caso, um texto não formatado. O serviço web retorna sua resposta na forma de uma única linha de texto:
    • <erreur>message</erreur> se houver um erro;
    • <impot>valeur</impot>, em que valeur é o valor do imposto.
  • linha 43: instanciação das camadas [dao] e [metier];
  • linhas 44-46: em caso de erro na instanciação, o script envia sua resposta e é encerrado;
  • linhas 49-53: o parâmetro 'params' é recuperado. Se estiver ausente, o script envia sua resposta e é encerrado;
  • linha 56: o parâmetro 'sim,2,200000' é dividido em 3 campos na matriz items;
  • linhas 58-77: a validade dos três campos é verificada. Se houver um erro, o script envia sua resposta e é encerrado;
  • linha 79: o imposto é calculado;
  • linha 81: e enviado ao cliente.

Em um navegador da web, obtêm-se os seguintes resultados:

13.2. Um cliente programado


O programa (client_impots_web_01)


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

import httplib,urllib,re

# constantes
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");

# conexão
connexion=httplib.HTTPConnection(HOST)
# acompanhamento
#connexion.set_debuglevel(1)
# é feito um ciclo sobre os dados a serem enviados ao servidor
for params in data:
    # os parâmetros devem ser codificados antes de serem enviados ao servidor
    parametres = urllib.urlencode({'params': params})
    # envio da solicitação
    connexion.request("POST",URL,parametres)
    # processamento da resposta (linha de texto)
    reponse=connexion.getresponse().read()
    lignes=reponse.split("\n")
    # a linha de texto tem o seguinte formato
    # <impot>xxx</impot>
    # ou <erro>xxx</erro>
    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
# encerramento da conexão
connexion.close()

13.3. Résultats

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