13. Ejercicio [IMPOTS] con un servicio WEB
Volvemos al ejercicio [IMPOTS] ya tratado en los apartados 4.2, 4.3, 8 y 10.
![]() |
13.1. El servidor
Nos situamos en la siguiente arquitectura cliente-servidor:
![]() |
En el lado del servidor, volveremos a utilizar una arquitectura de tres capas. La capa [dao] se implementará mediante la clase ImpotsMySQL utilizada en la version con la SGBD MySQL del apartado 10. La capa [metier] se implementará mediante la clase [ImpotsMetier] ya estudiada. Por lo tanto, solo queda escribir el servicio web. Este recibe de su clients un parámetro params con el formato params= sí,2,200000, donde el primer elemento indica si el contribuyente está casado o no, el segundo su número de hijos y el tercero su salario anual.
El servicio web es el siguiente (impots_web_01):
#!D:\Programas\ActivePython\Python2.7.2\python.exe
# -*- coding=Utf-8 -*-
# importación del módulo de clases Impuestos*
from impots import *
import cgi,cgitb,re
# se permite la visualización de información de depuración
cgitb.enable()
sys.stderr=sys.stdout
# ------------------------------------------------
# el servicio web de impuestos
# ------------------------------------------------
# los datos necesarios para el cálculo del impuesto se han colocado en la tabla mysqL TABLE
# perteneciente a la base de datos BASE. La tabla tiene la siguiente estructura
# límites decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2)
# los parámetros de las personas sujetas a impuestos (estado civil, número de hijos, salario anual)
# son enviados por el cliente en el formato params=estado civil, número de hijos, salario anual
# los resultados (estado civil, número de hijos, salario anual, impuesto a pagar) se devuelven al cliente
# en el formato <impuesto>valor</impuesto>
# o en el formato <error>msg</error>, si los parámetros no son válidos
# el servidor devuelve al cliente texto sin formato
print "Content-Type: text/plain\n"
# definición de las constantes
USER="root"
PWD=""
HOTE="localhost"
BASE="dbimpots"
TABLE="impots"
# se crea la capa [metier]
# Python no parece admitir sesiones
# en PHP, se habría utilizado una sesión para almacenar el objeto de negocio
# aquí, se construye sistemáticamente a partir de los datos de la base de datos MySQL
# instanciación de la capa [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()
# se recupera la línea enviada por el cliente al servidor
params=cgi.FieldStorage().getlist('params')
# si no hay parámetros, se produce un error
if not params:
print "<erreur>Le parametre [params] est absent<erreur>"
sys.exit()
# se procesa el parámetro params
items=params[0].strip().lower().split(',')
# solo debe haber 3 campos
if len(items)!=3:
print "<erreur>[%s] : nombre de parametres invalide</erreur>" % (params[0])
sys.exit()
# el primer parámetro (estado civil) debe ser sí/no
marie=items[0].strip()
if marie!="oui" and marie != "non":
print "<erreur>[%s] : 1er parametre invalide</erreur>\n"% (params[0])
sys.exit()
# el segundo parámetro (número de hijos) debe ser un número entero
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])
# el tercer parámetro (salario) debe ser un número entero
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])
# se calcula el impuesto
impot=metier.calculer(marie,enfants,salaire)
# se devuelve el resultado
print "<impot>%s</impot>\n" % (impot)
# fin
Notas:
- línea 5: se importan los objetos del archivo impots.py;
- línea 9: depurar un script CGI puede resultar problemático. El módulo cgitb envía, en caso de error, el motivo del fallo. Para ello, hay que redirigir la salida de error estándar (sys.stderr) a la salida estándar (sys.stdout) (línea 10);
- línea 26: el encabezado Http que determina la naturaleza del documento enviado, en este caso un texto sin formato. El servicio web devuelve su respuesta en forma de una única línea de texto:
- <error>mensaje</error> si hay un error;
- <impuesto>valor</impuesto>, donde valor es el importe del impuesto.
- línea 43: instanciación de las capas [dao] y [metier];
- líneas 44-46: en caso de error en la instanciación, el script envía su respuesta y finaliza;
- líneas 49-53: se recupera el parámetro «params». Si no está presente, el script envía su respuesta y finaliza;
- línea 56: el parámetro «oui,2,200000» se descompone en 3 campos en la matriz items;
- líneas 58-77: se comprueba la validez de los 3 campos. Si hay un error, el script envía su respuesta y finaliza;
- línea 79: se calcula el impuesto;
- línea 81: y se envía al cliente.
En un navegador web, se obtienen los siguientes resultados:
![]() | ![]() |
13.2. Un cliente programado
# -*- 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");
# conexión
connexion=httplib.HTTPConnection(HOST)
# seguimiento
#connexion.set_debuglevel(1)
# se recorre el bucle de los datos que se van a enviar al servidor
for params in data:
# los parámetros deben codificarse antes de enviarse al servidor
parametres = urllib.urlencode({'params': params})
# envío de la solicitud
connexion.request("POST",URL,parametres)
# procesamiento de la respuesta (línea de texto)
reponse=connexion.getresponse().read()
lignes=reponse.split("\n")
# la línea de texto tiene el formato
# <impot>xxx</impot>
# o <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
# cierre de la conexión
connexion.close()



