Skip to content

15. Вправа [IMPOTS] із XML

У цьому вправі, яке вже неодноразово розглядалося, сервер повертає результати клієнту у вигляді потоку XML:

  • <reponse><erreur>msg</erreur></reponse> у разі помилки;
  • <reponse><impot>valeur</impot></reponse>, якщо податок вдалося обчислити.

Ми використовуємо те, що щойно вивчили, для аналізу документа XML.

15.1. Веб-сервіс

Веб-сервіс нічим не відрізняється від розглянутого раніше, за винятком того, що відповідь XML, яка надсилається клієнту, дещо відрізняється. Архітектура залишається незмінною:


Веб-сервіс (impots_web_02)


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

# -*- coding=Utf-8 -*-
# імпорт модуля класів Impots*
from impots import *
import cgi,cgitb,re

# дозволено відображення інформації для налагодження
cgitb.enable()

# ------------------------------------------------ 
# веб-сервіс податків
# ------------------------------------------------ 

# дані, необхідні для розрахунку податку, розміщено в таблиці mysqL TABLE
#, що належить до бази даних BASE. Таблиця має таку структуру
# межі decimal(10,2), coeffR decimal(6,2), coeffN decimal(10,2)
# параметри осіб, що підлягають оподаткуванню (сімейний стан, кількість дітей, річний заробіток)
# надсилаються клієнтом у форматі params=сімейний стан, кількість дітей, річний заробіток
# результати (сімейний стан, кількість дітей, річний дохід, сума податку до сплати) надсилаються клієнту
# у форматі <податок>значення</податок>
# або у форматі <помилка>повідомлення</помилка>, якщо параметри недійсні

# сервер повертає клієнту неформатований текст
print "Content-Type: text/plain\n"
# початок відповіді
print "<reponse>"

# визначення констант
USER="root"
PWD=""
HOTE="localhost"
BASE="dbimpots"
TABLE="impots"

# інстанціювання шару [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()

# отримано рядок, надісланий клієнтом на сервер
params=cgi.FieldStorage().getlist('params')
# якщо параметрів немає, то виникає помилка
if not params:
    print "<erreur>Le parametre [params] est absent<erreur></reponse>"
    sys.exit()

# обробка параметра params  
#print "отримані параметри --> %s\n" %(params)
items=params[0].strip().lower().split(',')
# має бути лише 3 поля
if len(items)!=3:
    print "<erreur>[%s] : nombre de parametres invalide</erreur></reponse>" % (params[0])
    sys.exit()
# перший параметр (сімейний стан) має бути «так»/«ні»
marie=items[0].strip()
if marie!="oui" and marie != "non":
    print "<erreur>[%s] : 1er parametre invalide</erreur></reponse>\n"% (params[0])
    sys.exit()
# другий параметр (кількість дітей) повинен бути цілим числом
match=re.match(r"^\s*(\d+)\s*$",items[1])
if not match:
    print "<erreur>[%s] : 2ieme parametre invalide</erreur></reponse>\n"% (params[0])
    sys.exit()
enfants=int(match.groups()[0])
# третій параметр (зарплата) має бути цілим числом
match=re.match(r"^\s*(\d+)\s*$",items[2])
if not match:
    print "<erreur>[%s] : 2ieme parametre invalide</erreur></reponse>\n"% (params[0])
    sys.exit()
salaire=int(match.groups()[0])
# розраховується податок
impot=metier.calculer(marie,enfants,salaire)
# повертається результат
print "<impot>%s</impot></reponse>\n" % (impot)
# кінець

Примітки:

Цей веб-сервіс відрізняється від попереднього лише типом відповіді:

<reponse><erreur>msg</erreur></reponse> у разі помилки замість <erreur>msg</erreur>

<reponse><impot>valeur</impot></reponse>, якщо податок вдалося розрахувати, замість <impot>valeur</impot>

15.2. Програмований клієнт

Наш клієнт повинен проаналізувати відповідь XML, надіслану веб-сервісом. Ми застосовуємо те, що було розглянуто під час аналізу документа XML.


Програма (client_impots_web_02)


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

import httplib,urllib,re
import xml.sax, xml.sax.handler
# клас управління XML
class XmlHandler(xml.sax.handler.ContentHandler):

    # функція, що викликається при виявленні тегу початку
    def startElement(self,name,attributs):
        # записується поточний елемент
        global elementcourant
        elementcourant=name.strip().lower()

    # функція, що викликається при виявленні тегу закінчення
    def endElement(self,name):
        # нічого не робиться
        pass

    # функція обробки даних
    def characters(self,data):
        # дані
        global elementcourant,elements

        # дані отримуються
        match=re.match(r"^\s*(.+?)\s*$",data)
        if match:
            elements[elementcourant]=match.groups()[0].lower()
    
def getResultatsXml(reponse):
    # аналізується відповідь XML
    xml.sax.parseString(reponse,XmlHandler())
    # видаються результати
    if elements.has_key('erreur'):
        return (elements['erreur'],"")
    else:
        return ("",elements['impot'])

# ------------------------------------------------------------ main
# константи
HOST="localhost"
URL="/cgi-bin/impots_web_02b.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");
# глобальні змінні
elementcourant=""
elements={}

# з'єднання
connexion=httplib.HTTPConnection(HOST)
# відстеження
#connexion.set_debuglevel(1)
# цикл обробки даних, що надсилаються на сервер
for params in data:
    # параметри потрібно кодувати перед відправкою на сервер
    parametres = urllib.urlencode({'params': params})
    # відправлення запиту
    connexion.request("POST",URL,parametres)
    # обробка відповіді (потік XML)
    reponse=connexion.getresponse().read()
    # обробка XML-файлу
    (erreur,impot)=getResultatsXml(reponse)
    if not erreur:
        print "impot[%s]=%s" % (params,impot)
    else:
        print "erreur[%s]=%s" % (params,erreur)

Примітки:

  • потік XML веб-сервісу обробляється функцією getResultatsXml (рядок 60);
  • рядок 29: функція getResultatsXml;
  • рядок 31: відповідь XML веб-сервісу аналізується екземпляром класу XmlHandler, визначеного в рядку 6;
  • рядок 6: клас XmlHandler реалізує 3 методи: startElement, endElement, characters. За допомогою цих 3 методів створюється словник. Ключами є імена тегів <erreur> та <impot>, а значеннями — дані, пов’язані з цими двома тегами;
  • рядки 33–36: функція getResultatsXml повертає кортеж із 2 елементами:
    • (erreur,""), якщо аналіз потоку XML виявив наявність тегу <erreur>. У цьому випадку erreur представляє вміст цього тегу;
    • ("",impot), якщо аналіз потоку XML виявив наявність тегу <impot>. У цьому випадку impot представляє вміст цього тегу.
  • рядок 60: результат функції getResultatsXml отримується, а потім обробляється в рядках 61–64.

15.3. Результати

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