Skip to content

13. Übung [IMPOTS] mit einem Dienst WEB

Wir kehren zur Übung [IMPOTS] zurück, die bereits in den Abschnitten 4.2, 4.3, 8 und 10 behandelt wurde.

13.1. Der Server

Wir befinden uns in der folgenden Client-Server-Architektur:

Auf der Serverseite werden wir erneut eine dreischichtige Architektur verwenden. Die Schicht [dao] wird durch die Klasse ImpotsMySQL implementiert, die bereits in der Version mit SGBD und MySQL in Abschnitt 10 verwendet wurde. Die Schicht [metier] wird durch die bereits behandelte Klasse [ImpotsMetier] implementiert. Es bleibt also nur noch, den Webdienst zu schreiben. Dieser erhält von seinen Clients einen Parameter params in der Form params= ja,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 lautet wie folgt (impots_web_01):


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

# -*- coding=Utf-8 -*-
# Import des Moduls „Impots*“
from impots import *
import cgi,cgitb,re

# Anzeige von Debugging-Informationen zulassen
cgitb.enable()
sys.stderr=sys.stdout

# ------------------------------------------------ 
# Der Webdienst der Steuerbehörde
# ------------------------------------------------ 

# Die für die Steuerberechnung erforderlichen Daten wurden in die Tabelle mysqL TABLE
# der Datenbank BASE. Die Tabelle hat folgende Struktur
# Grenzwerte decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2)
# die Parameter der steuerpflichtigen Personen (Familienstand, Anzahl der Kinder, Jahresgehalt)
# werden vom Kunden in der Form „params=Familienstand, Anzahl der Kinder, Jahresgehalt“ übermittelt
# Die Ergebnisse (Familienstand, Anzahl der Kinder, Jahresgehalt, zu zahlende Steuer) werden an den Kunden zurückgesendet
# in der Form <Steuer>Wert</Steuer>
# oder in der Form <Fehler>Meldung</Fehler>, wenn die Parameter ungültig sind

# Der Server sendet unformatierten Text an den Client zurück
print "Content-Type: text/plain\n"

# Definition der Konstanten
USER="root"
PWD=""
HOTE="localhost"
BASE="dbimpots"
TABLE="impots"


# die Schicht [metier] wird erstellt
# Python scheint keine Unterstützung für Sitzungen zu bieten
# In PHP hätte man eine Sitzung verwendet, um das Geschäftsobjekt zu speichern
# Hier wird es systematisch aus den Daten der Datenbank erstellt MySQL

# Instanziierung der Schicht [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()

# die vom Client an den Server gesendete Zeile wird abgerufen
params=cgi.FieldStorage().getlist('params')
# Wenn keine Parameter vorhanden sind, wird ein Fehler ausgegeben
if not params:
  print "<erreur>Le parametre [params] est absent<erreur>"
  sys.exit()

# Der Parameter „params“ wird ausgewertet  
items=params[0].strip().lower().split(',')
# Es dürfen nur 3 Felder vorhanden sein
if len(items)!=3:
  print "<erreur>[%s] : nombre de parametres invalide</erreur>" % (params[0])
  sys.exit()
# Der erste Parameter (Familienstand) muss „ja“ oder „nein“ lauten
marie=items[0].strip()
if marie!="oui" and marie != "non":
  print "<erreur>[%s] : 1er parametre invalide</erreur>\n"% (params[0])
  sys.exit()
# Der zweite Parameter (Anzahl der Kinder) muss eine ganze Zahl sein
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])
# Der dritte Parameter (Gehalt) muss eine ganze Zahl sein
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])
# Die Steuer wird berechnet
impot=metier.calculer(marie,enfants,salaire)
# Das Ergebnis wird zurückgegeben
print "<impot>%s</impot>\n" % (impot)
# Ende

Hinweise:

  • Zeile 5: Die Objekte aus der Datei impots.py werden importiert;
  • Zeile 9: Das Debuggen eines CGI-Skripts kann problematisch sein. Das Modul cgitb gibt im Fehlerfall den Grund für den Absturz aus. Dazu muss die Standard-Fehlerausgabe (sys.stderr) in die Standardausgabe (sys.stdout) umgeleitet werden (Zeile 10);
  • Zeile 26: Der HTTP-Header, der die Art des gesendeten Dokuments festlegt, in diesem Fall unformatierter Text. Der Webdienst sendet seine Antwort in Form einer einzigen Textzeile zurück:
    • <erreur>message</erreur>, wenn ein Fehler vorliegt;
    • <impot>valeur</impot>, wobei valeur den Steuerbetrag angibt.
  • Zeile 43: Instanziierung der Schichten [dao] und [metier];
  • Zeilen 44–46: Bei einem Fehler bei der Instanziierung 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 „oui,2,200000“ wird im Array „items“ in drei Felder zerlegt;
  • 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 Kunden gesendet.

In einem Webbrowser erhält man folgende Ergebnisse:

13.2. Ein programmierter Client


Das Programm (client_impots_web_01)


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

import httplib,urllib,re

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

# Anmeldung
connexion=httplib.HTTPConnection(HOST)
# Nachverfolgung
#connexion.set_debuglevel(1)
# Es wird eine Schleife über die an den Server zu sendenden Daten durchlaufen
for params in data:
    # Die Parameter müssen vor dem Senden an den Server kodiert werden
    parametres = urllib.urlencode({'params': params})
    # Senden der Anfrage
    connexion.request("POST",URL,parametres)
    # Verarbeitung der Antwort (Textzeile)
    reponse=connexion.getresponse().read()
    lignes=reponse.split("\n")
    # Die Textzeile hat das folgende Format
    # <impot>xxx</impot>
    # oder <Fehler>xxx</Fehler>
    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
# Verbindung wird geschlossen
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