13. Übung [Steuerberechnung] mit einem Webdienst
Wir kehren zur Übung [TAXES] zurück, die bereits in den Abschnitten 4.2, 4.3, 8 und 10 behandelt wurde.
13.1. Der Server
Wir arbeiten mit der folgenden Client-Server-Architektur:
Auf der Serverseite verwenden wir erneut eine dreischichtige Architektur. Die [DAO]-Schicht wird durch die Klasse ImpotsMySQL implementiert, die in der Version mit dem DBMS MySQL in Abschnitt 10 verwendet wurde. Die [Business]-Schicht wird durch die bereits behandelte Klasse ImpotsMetier implementiert. Nun muss nur noch der Webservice geschrieben werden. Dieser Service erhält von seinen Clients einen params-Parameter in der Form params= yes,2,200000, wobei das erste Element angibt, ob der Steuerzahler verheiratet ist oder nicht, das zweite die Anzahl seiner Kinder und das dritte sein Jahresgehalt.
Der Webdienst sieht wie folgt aus (impots_web_01):
| # !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()
sys.stderr=sys.stdout
# ------------------------------------------------
# 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"
# definition of constants
USER="root"
PWD=""
HOTE="localhost"
BASE="dbimpots"
TABLE="impots"
# we create the [metier] layer
# Python doesn't seem to support sessions
# in PHP, we would have used a session to memorize the metier object
# here, it is systematically constructed from data in the MySQL database
# 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>"
sys.exit()
# we use the params parameter
items=params[0].strip().lower().split(',')
# there must be only 3 fields
if len(items)!=3:
print "<erreur>[%s] : nombre de parametres invalide</erreur>" % (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>\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>\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] : 3ieme parametre invalide</erreur>\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>\n" % (impot)
# end
|
Anmerkungen:
- Zeile 5: Importiere die Objekte aus der Datei impots.py;
- Zeile 9: Das Debuggen eines CGI-Skripts kann problematisch sein. Das Modul cgitb sendet im Fehlerfall den Grund für den Absturz. Dazu müssen Sie die Standard-Fehlerausgabe (sys.stderr) in die Standardausgabe (sys.stdout) umleiten (Zeile 10);
- Zeile 26: Der HTTP-Header, der die Art des gesendeten Dokuments angibt, in diesem Fall unformatierter Text. Der Webdienst gibt seine Antwort als einzelne Textzeile zurück:
- <error>message</error>, falls ein Fehler vorliegt;
- <tax>value</tax>, wobei „value“ der Steuerbetrag ist.
- Zeile 43: Instanziierung der Schichten [dao] und [business];
- Zeilen 44–46: Tritt ein Instanziierungsfehler auf, sendet das Skript seine Antwort und wird beendet;
- Zeilen 49–53: Der Parameter „params“ wird abgerufen. Fehlt er, sendet das Skript seine Antwort und wird beendet;
- Zeile 56: Der Parameter „yes,2,200000“ wird im Array „items“ in drei Felder aufgeteilt;
- Zeilen 58–77: Die Gültigkeit der drei Felder wird überprüft. Liegt ein Fehler vor, sendet das Skript seine Antwort und wird beendet;
- Zeile 79: Die Steuer wird berechnet;
- Zeile 81: und an den Client gesendet.
In einem Webbrowser werden folgende Ergebnisse angezeigt:
13.2. Ein programmierter Client
Das Programm (client_impots_web_01)
| # -*- coding=utf-8 -*-
import httplib,urllib,re
# constant
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");
# 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)
# answer processing (text line)
reponse=connexion.getresponse().read()
lignes=reponse.split("\n")
# the text line is of the form
# <impot>xxx</impot>
# or <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
# locking connection
connexion.close()
|
13.3. Ergebnisse
| 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
|