13. Ćwiczenie [IMPOTS] z wykorzystaniem usługi WEB
Wracamy do ćwiczenia [IMPOTS], omówionego już w punktach 4.2, 4.3, 8 i 10.
![]() |
13.1. Serwer
Rozważamy następującą architekturę klient-serwer:
![]() |
Po stronie serwera ponownie zastosujemy architekturę trójwarstwową. Warstwa [dao] zostanie zaimplementowana przez klasę ImpotsMySQL, wykorzystaną w wersji z SGBD i MySQL w punkcie 10. Warstwa [metier] zostanie zaimplementowana przez klasę [ImpotsMetier], którą już omówiliśmy. Pozostaje więc jedynie napisać usługę internetową. Usługa ta otrzymuje od swoich klientów parametr params w postaci params= tak,2,200000, gdzie pierwszy element wskazuje, czy podatnik jest w związku małżeńskim, drugi – liczbę dzieci, a trzeci – roczne wynagrodzenie.
Usługa internetowa wygląda następująco (impots_web_01):
#!D:\Programs\ActivePython\Python2.7.2\python.exe
# -*- coding=Utf-8 -*-
# import modułu klas Impots*
from impots import *
import cgi,cgitb,re
# zezwalamy na wyświetlanie informacji debugowania
cgitb.enable()
sys.stderr=sys.stdout
# ------------------------------------------------
# serwis internetowy urzędu skarbowego
# ------------------------------------------------
# dane niezbędne do obliczenia podatku zostały umieszczone w tabeli mysqL TABLE
# należącej do bazy danych BASE. Tabela ma następującą strukturę
# limity decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2)
# parametry osób podlegających opodatkowaniu (stan cywilny, liczba dzieci, roczne wynagrodzenie)
# są przesyłane przez klienta w postaci params=stan cywilny, liczba dzieci, roczne wynagrodzenie
# wyniki (stan cywilny, liczba dzieci, roczne wynagrodzenie, podatek do zapłaty) są zwracane klientowi
# w postaci <podatek>wartość</podatek>
# lub w postaci <błąd>msg</błąd>, jeśli parametry są nieprawidłowe
# serwer zwraca klientowi tekst nieformatowany
print "Content-Type: text/plain\n"
# definicja stałych
USER="root"
PWD=""
HOTE="localhost"
BASE="dbimpots"
TABLE="impots"
# tworzy się warstwę [metier]
# Wydaje się, że Python nie obsługuje sesji
# w PHP użyto by sesji do zapamiętania obiektu biznesowego
# tutaj obiekt ten jest systematycznie tworzony na podstawie danych z bazy danych MySQL
# instancjonowanie warstwy [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()
# pobieramy wiersz wysłany przez klienta do serwera
params=cgi.FieldStorage().getlist('params')
# jeśli nie ma parametrów, to zgłaszany jest błąd
if not params:
print "<erreur>Le parametre [params] est absent<erreur>"
sys.exit()
# przetwarzamy parametr „params”
items=params[0].strip().lower().split(',')
# musi zawierać tylko 3 pola
if len(items)!=3:
print "<erreur>[%s] : nombre de parametres invalide</erreur>" % (params[0])
sys.exit()
# pierwszy parametr (stan cywilny) musi mieć wartość „tak” lub „nie”
marie=items[0].strip()
if marie!="oui" and marie != "non":
print "<erreur>[%s] : 1er parametre invalide</erreur>\n"% (params[0])
sys.exit()
# drugi parametr (liczba dzieci) musi być liczbą całkowitą
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])
# trzeci parametr (wynagrodzenie) musi być liczbą całkowitą
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])
# obliczamy podatek
impot=metier.calculer(marie,enfants,salaire)
# zwracamy wynik
print "<impot>%s</impot>\n" % (impot)
# koniec
Uwagi:
- wiersz 5: importujemy obiekty z pliku impots.py;
- wiersz 9: debugowanie skryptu CGI może być problematyczne. Moduł cgitb wysyła, w przypadku błędu, przyczynę awarii. W tym celu należy przekierować standardowe wyjście błędów (sys.stderr) do standardowego wyjścia (sys.stdout) (wiersz 10);
- wiersz 26: nagłówek HTTP określający rodzaj wysyłanego dokumentu, w tym przypadku tekst nieformatowany. Serwis internetowy zwraca odpowiedź w postaci pojedynczego wiersza tekstu:
- <erreur>message</erreur>, jeśli wystąpi błąd;
- <impot>valeur</impot>, gdzie valeur to kwota podatku.
- wiersz 43: instancjonowanie warstw [dao] i [metier];
- wiersze 44–46: w przypadku błędu podczas instancjonowania skrypt wysyła odpowiedź i kończy działanie;
- wiersze 49–53: pobierany jest parametr „params”. Jeśli go nie ma, skrypt wysyła odpowiedź i kończy działanie;
- wiersz 56: parametr „oui,2,200000” jest rozkładany na 3 pola w tablicy items;
- wiersze 58–77: sprawdzana jest poprawność trzech pól. Jeśli wystąpi błąd, skrypt wysyła odpowiedź i kończy działanie;
- wiersz 79: obliczany jest podatek;
- wiersz 81: i wysyłany do klienta.
W przeglądarce internetowej otrzymujemy następujące wyniki:
![]() | ![]() |
13.2. Zaprogramowany klient
# -*- coding=utf-8 -*-
import httplib,urllib,re
# stałe
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");
# logowanie
connexion=httplib.HTTPConnection(HOST)
# monitorowanie
#connexion.set_debuglevel(1)
# przechodzimy do pętli przetwarzania danych, które mają zostać wysłane na serwer
for params in data:
# parametry muszą zostać zakodowane przed wysłaniem na serwer
parametres = urllib.urlencode({'params': params})
# wysyłanie żądania
connexion.request("POST",URL,parametres)
# przetwarzanie odpowiedzi (wiersz tekstu)
reponse=connexion.getresponse().read()
lignes=reponse.split("\n")
# wiersz tekstu ma postać
# <impot>xxx</impot>
# lub <błąd>xxx</błąd>
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
# zamknięcie połączenia
connexion.close()



