27. Anwendungsübung: Version 9
Wir kehren zu Version 7 der Anwendungsübung zurück, und statt JSON-Strings tauschen Client und Webserver nun XML-Daten aus.
Die Architektur bleibt unverändert:

27.1. Der Webserver

Der Ordner [http-servers/04] wird durch Kopieren des Ordners [http-servers/02] erstellt, wobei der Unterordner [utilities] ausgenommen bleibt. Anschließend werden die folgenden Elemente geändert:

Die Datei [config] wird wie folgt geändert:
# dépendances absolues
absolute_dependencies = [
# dossiers du projet
# BaseEntity, MyException
f"{root_dir}/classes/02/entities",
# InterfaceImpôtsDao, InterfaceImpôtsMétier, InterfaceImpôtsUi
f"{root_dir}/impots/v04/interfaces",
# AbstractImpôtsdao, ImpôtsConsole, ImpôtsMétier
f"{root_dir}/impots/v04/services",
# ImpotsDaoWithAdminDataInDatabase
f"{root_dir}/impots/v05/services",
# AdminData, ImpôtsError, TaxPayer
f"{root_dir}/impots/v04/entities",
# Constantes, tranches
f"{root_dir}/impots/v05/entities",
# index_controller
f"{root_dir}/impots/http-servers/01/controllers",
# scripts [config_database, config_layers]
script_dir,
# Logger, SendAdminMail
f"{root_dir}/impots/http-servers/02/utilities",
]
- Zeile 21: Geben Sie das Verzeichnis für die Dienstprogramme an, die in [http-servers/02] verbleiben;
Das Hauptskript [main] ändert sich wie folgt:
- Die einzige Änderung betrifft Zeile 23: Wir senden nun eine XML-Antwort;
Die Funktion [xml_response] ist im Modul [myutils] definiert:
- Zeile 3: Die Funktion [xml_response] nimmt als Parameter
- das in XML zu konvertierende [result]-Dictionary;
- den an den Web-Client zurückzugebenden Statuscode [status_code];
- Zeile 5: Wir verwenden die Bibliothek [xmltodict], um die XML-Zeichenkette zu generieren;
- Zeile 8: Wir verwenden den Header [Content-Type], um dem Client mitzuteilen, dass wir XML senden;
Die Funktion [xml_response] muss in das Skript [__init__.py] importiert werden:
from .myutils import set_syspath, json_response, decode_flask_session, xml_response
Anschließend muss das Modul [myutils] in die systemweiten Module aufgenommen werden. Dies erfolgt in einem PyCharm-Terminal mit dem Befehl [pip install .] (im Ordner „packages“).
27.2. Der Web-Client
27.2.1. Der Code
Der Ordner [http-clients/04] wird durch Kopieren des Clients [http-clients/02] erstellt. Anschließend ändern wir die Klasse [ImpôtsDaoWithHttpClient] wie folgt:
- Zeile 30: Die HTTP-Antwort [response] vom Webserver ist nun eine XML-Zeichenkette. Die Protokolle zeigen ihr Format:
2020-07-27 15:53:47.886283, Thread-2 : <?xml version="1.0" encoding="utf-8"?>
<réponse><result><marié>non</marié><enfants>2</enfants><salaire>100000</salaire><impôt>19884</impôt><surcôte>4480</surcôte><taux>0.41</taux><décôte>0</décôte><réduction>0</réduction></result></réponse>
Die Zeichenfolge [<?xml version="1.0" encoding="utf-8"?>] ist 38 Zeichen lang. Betrachtet man die Logdatei zudem mit einem Hex-Editor, sieht man, dass nach dieser Zeichenfolge ein Zeilenumbruchzeichen \n folgt. Danach folgt die Antwort <response>…</response>. Die XML-Zeichenfolge, die wir konvertieren müssen, beginnt daher nach den ersten 39 Zeichen der XML-Zeichenfolge. Sie beginnt bei Zeichen #39, wobei das erste Zeichen die Nummer 0 hat. Diese Zeichenfolge erhält man mit dem Ausdruck [response.text[39:]].
Wenn wir den Client ausführen (befolgen Sie die Vorgehensweise aus den vorherigen Beispielen), erhalten wir in der Datei [results.json] dieselben Ergebnisse wie in den vorherigen Versionen. Die Protokolle lauten wie folgt:
2020-07-27 16:21:14.015941, Thread-1 : début du thread [Thread-1] avec 2 contribuable(s)
2020-07-27 16:21:14.016940, Thread-1 : début du calcul de l'impôt de {"id": 1, "marié": "oui", "enfants": 2, "salaire": 55555}
2020-07-27 16:21:14.016940, Thread-2 : début du thread [Thread-2] avec 3 contribuable(s)
2020-07-27 16:21:14.018939, Thread-2 : début du calcul de l'impôt de {"id": 3, "marié": "oui", "enfants": 3, "salaire": 50000}
2020-07-27 16:21:14.019979, Thread-3 : début du thread [Thread-3] avec 3 contribuable(s)
2020-07-27 16:21:14.019979, Thread-3 : début du calcul de l'impôt de {"id": 6, "marié": "oui", "enfants": 3, "salaire": 100000}
2020-07-27 16:21:14.021938, Thread-4 : début du thread [Thread-4] avec 2 contribuable(s)
2020-07-27 16:21:14.021938, Thread-4 : début du calcul de l'impôt de {"id": 9, "marié": "oui", "enfants": 2, "salaire": 30000}
2020-07-27 16:21:14.021938, Thread-5 : début du thread [Thread-5] avec 1 contribuable(s)
2020-07-27 16:21:14.022939, Thread-5 : début du calcul de l'impôt de {"id": 11, "marié": "oui", "enfants": 3, "salaire": 200000}
2020-07-27 16:21:14.031942, Thread-1 : <?xml version="1.0" encoding="utf-8"?>
<réponse><result><marié>oui</marié><enfants>2</enfants><salaire>55555</salaire><impôt>2814</impôt><surcôte>0</surcôte><taux>0.14</taux><décôte>0</décôte><réduction>0</réduction></result></réponse>
2020-07-27 16:21:14.031942, Thread-1 : fin du calcul de l'impôt de {"id": 1, "marié": "oui", "enfants": 2, "salaire": 55555, "impôt": 2814, "surcôte": 0, "taux": 0.14, "décôte": 0, "réduction": 0}
2020-07-27 16:21:14.031942, Thread-1 : début du calcul de l'impôt de {"id": 2, "marié": "oui", "enfants": 2, "salaire": 50000}
2020-07-27 16:21:14.034941, Thread-4 : <?xml version="1.0" encoding="utf-8"?>
<réponse><result><marié>oui</marié><enfants>2</enfants><salaire>30000</salaire><impôt>0</impôt><surcôte>0</surcôte><taux>0.0</taux><décôte>0</décôte><réduction>0</réduction></result></réponse>
…
2020-07-27 16:21:17.055931, Thread-3 : fin du thread [Thread-3]
2020-07-27 16:21:17.059930, Thread-2 : <?xml version="1.0" encoding="utf-8"?>
<réponse><result><marié>non</marié><enfants>3</enfants><salaire>100000</salaire><impôt>16782</impôt><surcôte>7176</surcôte><taux>0.41</taux><décôte>0</décôte><réduction>0</réduction></result></réponse>
2020-07-27 16:21:17.060971, Thread-2 : fin du calcul de l'impôt de {"id": 5, "marié": "non", "enfants": 3, "salaire": 100000, "impôt": 16782, "surcôte": 7176, "taux": 0.41, "décôte": 0, "réduction": 0}
2020-07-27 16:21:17.060971, Thread-2 : fin du thread [Thread-2]
Auf der Serverseite sehen die Protokolle wie folgt aus:
2020-07-27 16:32:04.983020, Thread-46 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=oui&enfants=2&salaire=50000' [GET]>
2020-07-27 16:32:04.983020, Thread-46 : [index] mis en pause du thread pendant 1 seconde(s)
2020-07-27 16:32:04.984021, Thread-47 : [index] requête : <Request 'http://127.0.0.1:5000/?marié=oui&enfants=2&salaire=55555' [GET]>
2020-07-27 16:32:04.984021, Thread-47 : [index] mis en pause du thread pendant 1 seconde(s)
…
2020-07-27 16:32:07.001271, Thread-56 : [index] mis en pause du thread pendant 1 seconde(s)
2020-07-27 16:32:07.003078, Thread-54 : [index] {'réponse': {'result': {'marié': 'oui', 'enfants': 5, 'salaire': 100000, 'impôt': 4230, 'surcôte': 0, 'taux': 0.14, 'décôte': 0, 'réduction': 0}}}
2020-07-27 16:32:07.006078, Thread-55 : [index] {'réponse': {'result': {'marié': 'oui', 'enfants': 3, 'salaire': 200000, 'impôt': 42842, 'surcôte': 17283, 'taux': 0.41, 'décôte': 0, 'réduction': 0}}}
2020-07-27 16:32:08.002824, Thread-56 : [index] {'réponse': {'result': {'marié': 'non', 'enfants': 2, 'salaire': 100000, 'impôt': 19884, 'surcôte': 4480, 'taux': 0.41, 'décôte': 0, 'réduction': 0}}}
- Der Logger schreibt weiterhin das Antwort-Dictionary statt der an den Client gesendeten XML-Zeichenkette. Dies ist kein Fehler und beabsichtigt;
27.2.2. Testen der [DAO]-Schicht des Clients

Die Testklasse [TestHttpClientDao] ist dieselbe wie in |Version 7| und liefert die gleichen Ergebnisse.