30. Praktische Übung: Version 12
In diesem Kapitel werden wir eine Webanwendung nach der MVC-Architektur (Model-View-Controller) schreiben. Die Anwendung wird in der Lage sein, Antworten in drei Formaten zurückzugeben: JSON, XML und HTML. Der Komplexitätsgrad ist bei dem, was wir nun tun werden, deutlich höher als bei dem, was wir bisher gemacht haben. Wir werden die meisten der bisher behandelten Konzepte wiederverwenden und alle Schritte, die zur endgültigen Anwendung führen, detailliert beschreiben.
30.1. MVC-Architektur
Wir werden das MVC-Architekturmuster (Model–View–Controller) wie folgt implementieren:

Die Verarbeitung einer Client-Anfrage läuft wie folgt ab:
- 1 – Anfrage
Die angeforderten URLs haben die Form http://machine:port/action/param1/param2/… Der [Haupt-Controller] verwendet eine Konfigurationsdatei, um die Anfrage an den richtigen Controller weiterzuleiten. Dazu nutzt er das Feld [action] der URL. Der Rest der URL [param1/param2/…] besteht aus optionalen Parametern, die an die Aktion übergeben werden. Das „C“ in MVC bezieht sich hier auf die Kette [Hauptcontroller, Controller / Aktion]. Wenn kein Controller die angeforderte Aktion verarbeiten kann, antwortet der Webserver, dass die angeforderte URL nicht gefunden wurde.
- 2 – Verarbeitung
- Die ausgewählte Aktion [2a] kann die Parameter verwenden, die ihr vom [Haupt-Controller] übergeben wurden. Diese können aus zwei Quellen stammen:
- dem Pfad [/param1/param2/…] der URL,
- aus Parametern, die im Body der Client-Anfrage gesendet wurden;
- Bei der Verarbeitung der Benutzeranfrage benötigt die Aktion möglicherweise die [Business]-Schicht [2b]. Sobald die Anfrage des Clients verarbeitet wurde, kann dies verschiedene Antworten auslösen. Ein klassisches Beispiel ist:
- eine Fehlerantwort, wenn die Anfrage nicht korrekt verarbeitet werden konnte;
- ansonsten eine Bestätigungsantwort;
- Der [Controller / Action] sendet seine Antwort [2c] zusammen mit einem Statuscode an den Hauptcontroller zurück. Diese Statuscodes geben den aktuellen Zustand der Anwendung eindeutig wieder. Es handelt sich entweder um einen Erfolgscode oder einen Fehlercode;
- 3 – Antwort
- Je nachdem, ob der Client eine JSON-, XML- oder HTML-Antwort angefordert hat, instanziiert der [Hauptcontroller] [3a] den entsprechenden Antworttyp und weist ihn an, die Antwort an den Client zu senden. Der [Hauptcontroller] übergibt ihm sowohl die Antwort als auch den Statuscode, der von dem ausgeführten [Controller/Action] bereitgestellt wurde;
- Wenn die gewünschte Antwort vom Typ JSON oder XML ist, formatiert die ausgewählte Antwort die Antwort des [Controllers/der Aktion], die ihr übermittelt wurde, und sendet sie [3c]. Der Client, der diese Antwort verarbeiten kann, kann ein Python-Konsolenskript oder ein in eine HTML-Seite eingebettetes JavaScript-Skript sein;
- Wenn die gewünschte Antwort vom Typ HTML ist, wählt die ausgewählte Antwort [3b] anhand des ihr übermittelten Statuscodes eine der HTML-Ansichten [Vuei] aus. Dies ist das V in MVC. Eine einzelne Ansicht entspricht einem einzelnen Statuscode. Diese Ansicht V zeigt die Antwort des [Controllers/der Aktion] an, die ausgeführt wurde. Sie verpackt die Daten dieser Antwort in HTML, CSS und JavaScript. Diese Daten werden als View-Modell bezeichnet. Dies ist das M in MVC. Der Client ist meist ein Browser;
Lassen Sie uns nun die Beziehung zwischen der MVC-Webarchitektur und der Schichtenarchitektur klären. Je nachdem, wie das Modell definiert ist, können diese beiden Konzepte miteinander in Verbindung stehen oder auch nicht. Betrachten wir eine einschichtige MVC-Webanwendung:

Im obigen Beispiel enthalten der [Controller / die Aktion] jeweils Teile der [Business-] und [DAO-]Schichten. In der [Web-]Schicht haben wir zwar eine MVC-Architektur, aber die Anwendung als Ganzes verfügt nicht über eine Schichtenarchitektur. Hier gibt es nur eine Schicht – die Web-Schicht –, die alles abwickelt.
Betrachten wir nun eine mehrschichtige Webarchitektur:

Die [Web]-Schicht kann implementiert werden, ohne dem MVC-Modell zu folgen. Wir haben dann eine mehrschichtige Architektur, aber die Web-Schicht implementiert das MVC-Modell nicht.
In der .NET-Welt kann die oben genannte [Web]-Schicht beispielsweise mit ASP.NET MVC implementiert werden, was zu einer mehrschichtigen Architektur mit einer [Web]-Schicht im MVC-Stil führt. Anschließend können wir diese ASP.NET MVC-Schicht durch eine klassische ASP.NET-Schicht (WebForms) ersetzen, während der Rest (Geschäftslogik, DAO, Treiber) unverändert bleibt. Wir haben dann eine Schichtenarchitektur mit einer [Web]-Schicht, die nicht mehr MVC-basiert ist.
In MVC haben wir gesagt, dass das M-Modell das der V-Ansicht ist, d. h. die Menge der von der V-Ansicht angezeigten Daten. Es gibt eine weitere Definition des M-Modells in MVC:

Viele Autoren sind der Ansicht, dass das, was rechts von der [Web]-Schicht liegt, das M-Modell von MVC bildet. Um Mehrdeutigkeiten zu vermeiden, können wir uns auf Folgendes beziehen:
- das Domänenmodell, wenn wir uns auf alles rechts von der [Web-]Schicht beziehen;
- das View-Modell, wenn wir uns auf die von einer View V angezeigten Daten beziehen;
Im Folgenden beziehen wir uns mit dem Begriff „Modell“ immer auf das View-Modell.
30.2. Client/Server-Anwendungsarchitektur
Die Webanwendung wird die folgende Architektur aufweisen:

- In [1] wird der Webserver zwei Arten von Clients haben:
- in [2] einen Konsolen-Client, der JSON und XML mit dem Server austauscht;
- in [3] einen Browser, der HTML vom Server empfängt und anzeigt;
- Der Webserver [1] behält die [Business]- und [DAO]-Schichten aus früheren Versionen bei;
- Der Web-Client [2] wird aktualisiert, um die neuen Service-URLs der Webanwendung zu berücksichtigen;
- Die vom Browser angezeigte HTML-Anwendung muss von Grund auf neu geschrieben werden;
Wir werden die Anwendung in mehreren Phasen entwickeln:
- Wir werden die JSON-Version des Servers entwickeln. Wir werden die Service-URLs des Servers nacheinander mit einem Postman-Client testen. Diese Methode ermöglicht es uns, das Framework des Webservers aufzubauen, ohne uns um die Ansichten der Anwendung (=HTML) kümmern zu müssen;
- Nachdem wir den JSON-Server mit Postman getestet haben, werden wir ihn mit einem Konsolen-Client testen;
- Anschließend wenden wir uns der XML-Version des Servers zu. Wir haben gesehen, dass der Wechsel von JSON zu XML unkompliziert ist;
- schließlich werden wir uns der HTML-Version des Servers zuwenden. Wir werden eine MVC-Architektur aufbauen und die anzuzeigenden Ansichten definieren. Die HTML-Anwendung wird sowohl mit dem Postman-Client als auch mit einem Standardbrowser getestet;
30.3. Die Verzeichnisstruktur des Servercodes

- in [1: der Webserver als Ganzes;
- in [2]: Vorerst ignorieren wir die Ordner [static, templates, tests_views], die zur HTML-Version des Servers gehören. Außerhalb dieses Ordners finden wir das Hauptskript [main] und dessen Konfiguration;
- in [3] die Webserver-Controller. Dabei handelt es sich um Klasseninstanzen;
![]() | ![]() |
- in [4] wird die HTTP-Antwort des Servers von Klassen verarbeitet;
- in [5] behalten wir die Protokolldatei der vorherigen Server bei;
Wenn wir die HTML-Version des Servers erstellen, kommen weitere Ordner ins Spiel:
![]() | ![]() |
- in [6] die statischen Elemente der HTML-Anwendung;
- in [7] die HTML-Anwendungsvorlagen, unterteilt in Ansichten [9] und Ansichtsfragmente [8];
- in [9] die Klassen, die die Ansichtsmodelle implementieren;
30.4. Die Service-URLs der Anwendung
Um den Webserver zu erstellen, gehen wir wie folgt vor:
- Auf der Grundlage der Ansichten der HTML-Anwendung definieren wir die Aktionen, die die Webanwendung implementieren muss. Wir verwenden hier die tatsächlichen Ansichten, es könnten aber auch einfach Ansichten auf Papier sein;
- Basierend auf diesen Aktionen definieren wir die Service-URLs der HTML-Anwendung;
- Wir werden diese Service-URLs mithilfe eines Servers implementieren, der JSON zurückgibt. So können wir das Framework des Webservers definieren, ohne uns um die bereitzustellenden HTML-Seiten kümmern zu müssen. Wir werden diese Service-URLs mit Postman testen;
- Anschließend testen wir unseren JSON-Server mit einem Konsolen-Client;
- Sobald der JSON-Server validiert wurde, fahren wir mit dem Schreiben der HTML-Anwendung fort;
Die erste Ansicht wird die Authentifizierungsansicht sein:

- Die Aktion, die zu dieser ersten Ansicht führt, heißt [init-session] [1];
- Ein Klick auf die Schaltfläche [Validate] löst die Aktion [authenticate-user] mit zwei übermittelten Parametern aus [2-3];
Die Ansicht zur Steuerberechnung:

- In [1] die Aktion [authenticate-user], die zu dieser Ansicht geführt hat;
- Bei [2] löst ein Klick auf die Schaltfläche [Validate] die Ausführung der Aktion [calculate-tax] mit drei übermittelten Parametern aus [2-5];
- Ein Klick auf den Link [6] löst die Aktion [list-simulations] ohne Parameter aus;
- Ein Klick auf den Link [7] löst die Aktion [end-session] ohne Parameter aus;
Die dritte Ansicht zeigt die vom authentifizierten Benutzer durchgeführten Simulationen an:

- in [3] die Aktion [list-simulations], die zu dieser Ansicht geführt hat;
- in [2] löst das Klicken auf den Link [Löschen] die Aktion [delete-simulation] mit einem Parameter aus: der Nummer der Simulation, die aus der Liste gelöscht werden soll;
- Ein Klick auf den Link [3] löst die Aktion [display-tax-calculation] ohne Parameter aus, wodurch die Ansicht zur Steuerberechnung erneut angezeigt wird;
- Ein Klick auf den Link [4] löst die Aktion [end-session] ohne Parameter aus;
Mit diesen ersten Informationen können wir die verschiedenen Service-URLs des Servers definieren:
Aktion | Rolle | Ausführungskontext |
/init-session | Wird verwendet, um den Typ (json, xml, html) der gewünschten Antworten festzulegen | GET-Anfrage Kann jederzeit gesendet werden |
/authenticate-user | Autorisiert oder verweigert die Anmeldung eines Benutzers | POST-Anfrage. Die Anfrage muss zwei übermittelte Parameter enthalten [user, password] Kann nur gesendet werden, wenn der Sitzungstyp (json, xml, html) bekannt ist |
/calculate-tax | Führt eine Simulation der Steuerberechnung durch | POST-Anfrage. Die Anfrage muss drei übermittelte Parameter enthalten [verheiratet, Kinder, Gehalt] Kann nur ausgegeben werden, wenn der Sitzungstyp (json, xml, html) bekannt ist und der Benutzer authentifiziert ist |
/list-simulations | Anfrage zum Anzeigen der Liste der seit Beginn der Sitzung durchgeführten Simulationen | GET-Anfrage. Kann nur gesendet werden, wenn der Sitzungstyp (json, xml, html) bekannt ist und der Benutzer authentifiziert ist |
/delete-simulation/number | Löscht eine Simulation aus der Liste der Simulationen | GET-Anfrage. Kann nur gesendet werden, wenn der Sitzungstyp (json, xml, html) bekannt ist und der Benutzer authentifiziert ist |
/display-tax-calculation | Zeigt die HTML-Seite für die Steuerberechnung an | GET-Anfrage. Kann nur ausgegeben werden, wenn der Sitzungstyp (json, xml, html) bekannt ist und der Benutzer authentifiziert ist |
/end-session | Beendet die Simulationssitzung. | Technisch gesehen wird die alte Web-Sitzung gelöscht und eine neue Sitzung erstellt Kann nur ausgegeben werden, wenn der Sitzungstyp (json, xml, html) bekannt ist und der Benutzer authentifiziert ist |
Diese verschiedenen Service-URLs werden sowohl für den HTML-Server als auch für die JSON- oder XML-Server verwendet. Zwei URLs werden ausschließlich für die beiden letztgenannten Server verwendet: Es handelt sich um die URLs aus der vorherigen Version des Client-/Webservers, die wir hier wiederverwenden:
Aktion | Rolle | Ausführungskontext |
/get-admindata | Gibt Steuerdaten zurück, die zur Berechnung von der Steuer | GET-Anfrage. Wird nur verwendet, wenn der Sitzungstyp „json“ oder „xml“ ist. Der Benutzer muss authentifiziert sein |
/calculate-taxes | Berechnet die Steuer für eine Liste von Steuerzahlern, die in einer JSON- | GET-Anfrage übermittelt wurde. Wird nur verwendet, wenn der Sitzungstyp „json“ oder „xml“ ist. Der Benutzer muss authentifiziert sein |
Alle mit diesen Aktionen verbundenen Controller verfahren auf die gleiche Weise:
- Sie überprüfen ihre Parameter. Diese befinden sich im Objekt:
- [request.path] für Parameter, die in der URL in der Form [/action/param1/param2/…] vorhanden sind;
- im Objekt [request.form] für diejenigen, die als [x-www-form-urlencoded] im Request-Body übermittelt werden;
- im Objekt [request.data] für diejenigen, die als JSON im Request-Body übertragen werden;
- Ein Controller ähnelt einer Funktion oder Methode, die die Gültigkeit ihrer Parameter überprüft. Für den Controller ist es jedoch etwas komplizierter:
- Die erwarteten Parameter fehlen möglicherweise;
- Die vom Controller abgerufenen Parameter sind Zeichenfolgen. Wenn der erwartete Parameter eine Zahl ist, muss der Controller überprüfen, ob die Zeichenfolge des Parameters tatsächlich eine Zahl darstellt;
- Sobald überprüft wurde, dass die erwarteten Parameter vorhanden und syntaktisch korrekt sind, müssen Sie sicherstellen, dass sie im aktuellen Ausführungskontext gültig sind. Dieser Kontext ist in der Sitzung vorhanden. Das Authentifizierungsbeispiel ist ein Beispiel für einen Ausführungskontext. Bestimmte Aktionen sollten erst verarbeitet werden, wenn der Client authentifiziert wurde. Im Allgemeinen gibt ein Schlüssel in der Sitzung an, ob diese Authentifizierung stattgefunden hat oder nicht;
- Sobald die vorangegangenen Prüfungen abgeschlossen sind, kann der sekundäre Controller fortfahren. Dieser Prozess der Parameterüberprüfung ist sehr wichtig. Wir können nicht akzeptieren, dass ein Client uns zu irgendeinem Zeitpunkt während des Lebenszyklus der Anwendung beliebige Daten sendet. Wir müssen die volle Kontrolle über den Lebenszyklus der Anwendung behalten;
- Sobald seine Arbeit erledigt ist, gibt der sekundäre Controller ein Wörterbuch mit den Schlüsseln [action, state, response] an den Hauptcontroller zurück, der ihn aufgerufen hat:
- [action] ist die Aktion, die gerade ausgeführt wurde;
- [state] ist eine dreistellige Zahl, die das Ergebnis der Verarbeitung der Aktion angibt:
- [x00] steht für eine erfolgreiche Verarbeitung;
- [x01] zeigt einen Verarbeitungsfehler an;
- [response] ist das Wörterbuch der Ergebnisse in der Form {‘response’:object}. Das Objekt hat je nach der verarbeiteten Aktion unterschiedliche Strukturen;
Wir werden nun die verschiedenen Controller – oder, mit anderen Worten, die verschiedenen Aktionen, die diese Controller verarbeiten – betrachten, die den Workflow der Webanwendung steuern.
30.5. Serverkonfiguration

Die Datenbankkonfiguration [config_database] und die Konfiguration der Server-Ebene [config_layers] sind identisch mit denen in früheren Versionen. Die Datei [config] enthält nun neue Informationen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | |
- Bis Zeile 41 sehen wir Standardelemente;
- Zeilen 43–66: In Zeile 43 wird der Python-Pfad des Servers definiert. Anschließend können wir die Abhängigkeiten des Projekts importieren:
- Zeilen 45–55: die Liste der Controller;
- Zeilen 57–60: die Liste der HTTP-Antworten;
- Zeilen 62–66: die Liste der View-Vorlagen;
- Zeilen 68–189: die Anwendungskonfiguration mit einer Reihe von Konstanten;
- Zeilen 71–98: Diese Zeilen kennen wir bereits aus früheren Versionen;
- Zeilen 101–122: das Controller-Wörterbuch:
- Die Schlüssel sind die Namen der Aktionen;
- die Werte sind eine Instanz des Controllers, der für die Abwicklung dieser Aktion zuständig ist. Jeder Controller wird als einzelne Instanz (Singleton) instanziiert. Dieselbe Instanz wird von verschiedenen Server-Threads ausgeführt. Daher muss bei gemeinsam genutzten Daten, die jeder Controller möglicherweise ändern möchte, Vorsicht walten;
- Zeilen 125–129: das Wörterbuch der drei möglichen HTTP-Antworten:
- Die Schlüssel sind die vom Client angeforderten Antworttypen (JSON, XML, HTML);
- Die Werte sind eine Instanz der HTTP-Antwort. Jeder Antwortgenerator wird als einzelne Instanz (Singleton) instanziiert. Derselbe Generator wird von verschiedenen Server-Threads ausgeführt. Daher ist bei gemeinsam genutzten Daten, die jeder Generator möglicherweise ändern möchte, Vorsicht geboten;
- Zeilen 132–186: Konfiguration der HTML-Ansichten. Diese Zeilen lassen wir vorerst außer Acht;
- Zeilen 191–202: Diese Zeilen sind uns bereits aus früheren Versionen bekannt;
30.6. Der Pfad einer Client-Anfrage innerhalb des Servers

Wir werden den Weg einer Client-Anfrage, die beim Server eintrifft, bis hin zur zurückgesendeten HTTP-Antwort verfolgen. Er folgt dem Ablauf des MVC-Servers.
30.6.1. Das [main]-Skript
Das [main]-Skript ist in vielerlei Hinsicht identisch mit dem der vorherigen Versionen. Wir stellen es dennoch vollständig zur Verfügung, um sicherzustellen, dass wir auf dem richtigen Fuß starten:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | |
- Zeilen 1–92: Alle diese Zeilen wurden bereits behandelt und erklärt;
- Zeile 92: Der Server verwaltet eine Sitzung. Wir benötigen daher einen geheimen Schlüssel. Für jeden Benutzer speichern wir zwei Informationen in der Sitzung:
- ob sich der Benutzer erfolgreich authentifiziert hat;
- Jedes Mal, wenn eine Steuerberechnung durchgeführt wird, werden die Ergebnisse dieser Berechnung in eine Liste namens „Simulationsliste des Benutzers“ aufgenommen. Diese Liste wird in der Sitzung gespeichert;
- Zeilen 100–151: die Liste der Service-URLs des Servers. Die zugehörigen Funktionen dienen als Filter: Alle URLs, die nicht in dieser Liste enthalten sind, werden vom Flask-Server mit einem [404 NOT FOUND]-Fehler zurückgewiesen. Sobald diese Filterung abgeschlossen ist, wird die Anfrage systematisch an einen „Front Controller“ weitergeleitet, der durch die Funktion [front_controller] in den Zeilen 94–98 implementiert wird, auf die wir gleich noch eingehen werden;
- Zeilen 100–103: Behandlung der Route [/]. Der Einstiegspunkt für die Webanwendung ist die URL in Zeile 107. Daher leiten wir in Zeile 103 den Client zu dieser URL weiter:
- Die Funktion [url_for] wird in Zeile 18 importiert. Sie hat hier zwei Parameter:
- Der erste Parameter ist der Name einer der Routing-Funktionen, in diesem Fall die in Zeile 107. Wir sehen, dass diese Funktion einen Parameter [type_response] erwartet, bei dem es sich um den vom Client angeforderten Antworttyp (json, xml, html) handelt;
- der zweite Parameter übernimmt den Namen des Parameters aus Zeile 107, [type_response], und weist ihm einen Wert zu. Gäbe es weitere Parameter, würden wir den Vorgang für jeden einzelnen wiederholen;
- sie gibt die URL zurück, die mit der Funktion verknüpft ist, die durch die beiden ihr übergebenen Parameter bezeichnet wird. Hier gibt dies die URL aus Zeile 106 zurück, wobei der Parameter durch seinen Wert [/init-session/html] ersetzt wird;
- Die Funktion [redirect] wurde in Zeile 18 importiert. Ihre Aufgabe ist es, einen HTTP-Redirect-Header an den Client zu senden:
- Der erste Parameter ist die URL, zu der der Client umgeleitet werden soll;
- der zweite Parameter ist der Statuscode der an den Client gesendeten HTTP-Antwort. Der Code [status.HTTP_302_FOUND] entspricht einer HTTP-Weiterleitung;
Die Funktion [ front_controller] in den Zeilen 94–98 führt die erste Verarbeitung der Anfrage des Clients durch:
- Zeilen 1–57: Dieser Code ist uns bekannt. So war dies beispielsweise der Code für die Funktion namens [main] im Skript [main] der vorherigen Version. Zu beachten ist der in den Zeilen 25–26 verwendete Controller:
- Zeile 25: Wir rufen die Controller-Instanz mit dem Namen [main-controller] aus der Konfiguration ab. Dies sind die folgenden Zeilen:
- (Fortsetzung)
- Zeile 10 oben: Beachten Sie, dass wir eine Instanz der Klasse abrufen;
- Zeile 26: Wir weisen den Controller [MainController] an, die Anfrage zu bearbeiten;
- Zeilen 30–45: Die vom [MainController] zurückgegebene Antwort wird an den Client gesendet. Wir werden etwas später auf diese Zeilen zurückkommen;
Die Aufgabe der Funktion [front_controller] und anschließend der Klasse [MainController] besteht darin, die für alle Anfragen gemeinsamen Aufgaben zu bearbeiten:
Im obigen Diagramm befinden wir uns noch in Phase 1 der Anforderungsverarbeitung. Der Hauptcontroller [MainController] fährt mit Schritt 1 fort.

30.6.2. Der Hauptcontroller [MainController]
Der Hauptcontroller [MainController] setzt die von der Funktion [front_controller] begonnene Arbeit fort:
Alle Controller implementieren die folgende Schnittstelle [InterfaceController] [2]:

from abc import ABC, abstractmethod
from werkzeug.local import LocalProxy
class InterfaceController(ABC):
@abstractmethod
def execute(self, request: LocalProxy, session: LocalProxy, config: dict) -> (dict, int):
pass
- Die Schnittstelle [InterfaceController] definiert in Zeile 8 lediglich die einzige Methode [execute]. Diese Methode nimmt drei Parameter entgegen:
- [request]: die Anfrage des Clients;
- [session]: die Sitzung des Clients;
- [config]: die Anwendungskonfiguration;
Die Methode [execute] gibt ein Tupel mit zwei Elementen zurück:
- Das erste Element ist das Ergebniswörterbuch in der Form {‘action’: action, ‘status’: status, ‘response’: results};
- das zweite ist der HTTP-Statuscode, der an den Client zurückgegeben wird;
Der Hauptcontroller [MainController] [1] implementiert die Schnittstelle [InterfaceController] wie folgt:
Der [MainController] führt die ersten Prüfungen durch, um die Anfrage zu validieren.
- Zeilen 11–13: Der Controller beginnt damit, die vom Client angeforderte Aktion abzurufen. Zur Erinnerung: Service-URLs haben die Form [/action/param1/param2/…] und diese URL befindet sich in [request.path];
- Zeilen 17–23: Die Aktion [init-session] wird verwendet, um den vom Client angeforderten Antworttyp (json, xml, html) zu initialisieren. Diese Information wird in der Sitzung unter dem Schlüssel [responseType] gespeichert. Wenn die Aktion also nicht [init-session] ist, muss die Sitzung den Schlüssel [responseType] enthalten; andernfalls ist die Anfrage ungültig;
- Zeilen 21–22: Die Struktur des von jedem Controller zurückgegebenen Ergebnisses, in diesem Fall ein Fehlerergebnis:
- [action]: ist der Name der aktuellen Aktion. Dies ermöglicht es uns, den Namen bei der Protokollierung des Anforderungsergebnisses abzurufen;
- [status]: ist ein dreistelliger Statuscode:
- [x00] für einen Erfolg;
- [x01] für einen Fehler;
- [response]: ist die Antwort auf die Anfrage. Ihre Art ist spezifisch für jede Anfrage;
- Zeilen 24–30: Die Aktion [authenticate-user] dient zur Authentifizierung des Benutzers. Bei Erfolg wird der Schlüssel [user=True] zur Sitzung des Benutzers hinzugefügt. Bestimmte Service-URLs sind nur für authentifizierte Benutzer zugänglich. Dies wird hier überprüft;
- Zeile 26: Nur die Aktionen [init-session] und [authenticate-user] können von einem noch nicht authentifizierten Benutzer ausgeführt werden;
- Zeilen 28–29: Die im Fehlerfall zu sendende Antwort;
- Zeilen 32–34: Wenn einer der beiden vorherigen Fehler aufgetreten ist, wird die Fehlerantwort mit dem HTTP-Status 400 BAD REQUEST an den Client gesendet;
- Zeilen 35–39: Wenn kein Fehler aufgetreten ist, wird die Steuerung an den Controller übergeben, der für die Bearbeitung der aktuellen Aktion zuständig ist. Seine Instanz ist in der Anwendungskonfiguration zu finden;
Die Klasse [MainController] setzt die Arbeit der Funktion [front_controller] fort: Gemeinsam bearbeiten sie alles, was aus der Anforderungsverarbeitung herausgelöst werden kann, und warten bis zum letzten Moment, um die Anforderung an einen bestimmten Controller weiterzuleiten. Die Aufteilung des Codes zwischen der Funktion [front_controller] und der Klasse [MainController] ist rein subjektiv. Hier wollte ich die Struktur der vorherigen Version beibehalten: Die Funktion [front_controller] existierte bereits unter dem Namen [main]. In der Praxis könnte man:
- alles in die Funktion [front_controller] packen und die Klasse [MainController] entfernen;
- alles in die Klasse [MainController] packen und die Funktion [front_controller] entfernen. Ich würde eher diese Lösung wählen, da sie den Vorteil hat, den Code des Hauptskripts [main] zu straffen;
30.7. Aktionsspezifische Verarbeitung
Kehren wir zur MVC-Architektur der Anwendung zurück:

Wir befinden uns immer noch bei Schritt 1 oben. Wenn keine Fehler aufgetreten sind, beginnt Schritt 2. Die Anfrage wurde an den Controller weitergeleitet, der für die in der Anfrage angeforderte Aktion zuständig ist. Nehmen wir an, diese Aktion ist [/init-session], definiert durch die Route:
Diese Aktion ist in der Konfiguration [config] mit einem Controller verknüpft:
# actions autorisées et leurs contrôleurs
"controllers": {
# initialisation d'une session de calcul
"init-session": InitSessionController(),
…
},
Dann übernimmt der [InitSessionController] (Zeile 4). Sein Code lautet wie folgt:
- Zeile 6: Wie die anderen Controller implementiert auch der [InitSessionController] die Schnittstelle [InterfaceController];
- Zeile 10: Die URL hat den Typ [/init-session/type_response]. Wir rufen die Aktion [init-session] und den gewünschten Antworttyp ab;
- Zeile 15: Der gewünschte Antworttyp kann nur einer der in der Antwortkonfiguration vorhandenen sein:
# les différents types de réponse (json, xml, html)
"responses": {
"json": JsonResponse(),
"html": HtmlResponse(),
"xml": XmlResponse()
},
- ist dies nicht der Fall, wird eine Fehlerantwort 701 vorbereitet (Zeile 17);
- Zeilen 20–25: Fall, in dem der gewünschte Antworttyp gültig ist;
- Zeile 22: Der gewünschte Antworttyp wird in der Sitzung gespeichert. Dies geschieht, da wir ihn für nachfolgende Anfragen benötigen;
- Zeilen 23–24: Eine 700-Erfolgsantwort vorbereiten;
- Zeile 25: Die Erfolgsmeldung wird an den Aufrufer zurückgegeben;
- Zeile 27: Wenn ein Fehler aufgetreten ist, wird die Fehlerantwort an den Aufrufer zurückgegeben;
30.8. Generieren der HTTP-Antwort des Servers
Kehren wir zur MVC-Architektur der Anwendung zurück:

Wir haben gerade die Schritte 1 und 2 behandelt. Dabei sind wir auf drei Statuscodes gestoßen:
- 700: /init-session erfolgreich;
- 701: /init-session fehlgeschlagen;
- 101: ungültige Anfrage, entweder weil die Sitzung nicht initialisiert wurde oder weil der Benutzer nicht authentifiziert ist;
Schauen wir uns an, wie die Antwort des Servers im obigen Schritt 3 an den Client gesendet wird. Dies geschieht in der Funktion [front_controller] des Skripts [main]:
- Wir befinden uns nun in Zeile 26: Der Hauptcontroller hat seine Fehlerantwort zurückgegeben;
- Zeilen 27–29: Unabhängig von der Antwort des Hauptcontrollers (erfolgreich oder fehlgeschlagen) wird diese Antwort in der Protokolldatei protokolliert;
- Zeilen 30–33: Wie in früheren Versionen senden wir, wenn der HTTP-Status [500 INTERNAL SERVER ERROR] lautet, eine E-Mail mit dem Fehlerprotokoll an den Anwendungsadministrator;
- Zeilen 34–39: Wir senden die HTTP-Antwort, und das vom Controller zurückgegebene Ergebnis wird in den Hauptteil dieser Antwort eingefügt. Wir müssen wissen, in welchem Format (JSON, XML, HTML) der Client diese Antwort wünscht. Wir suchen in der Sitzung nach dem gewünschten Antworttyp. Ist dieser nicht vorhanden, setzen wir diesen Typ willkürlich auf JSON;
- Zeilen 40–43: Die HTTP-Antwort wird erstellt;
In der Konfigurationsdatei wurde jeder Antworttyp (json, xml, html) einer Klasseninstanz zugeordnet:
# les différents types de réponse (json, xml, html)
"responses": {
"json": JsonResponse(),
"html": HtmlResponse(),
"xml": XmlResponse()
},
Die Antwortklassen befinden sich im Ordner [responses] der Server-Verzeichnisstruktur:

Jede Antwortklasse implementiert die folgende [InterfaceResponse]-Schnittstelle:
from abc import ABC, abstractmethod
from flask.wrappers import Response
from werkzeug.local import LocalProxy
class InterfaceResponse(ABC):
@abstractmethod
def build_http_response(self, request: LocalProxy, session: LocalProxy, config: dict, status_code: int,
résultat: dict) -> (Response, int):
pass
- Zeilen 8–11: Die Schnittstelle [InterfaceResponse] definiert eine einzige Methode [build_http_response] mit den folgenden Parametern:
- [request, session, config]: Dies sind die vom Aktionscontroller empfangenen Parameter;
- [result, status_code]: Dies sind die vom Aktionscontroller erzeugten Ergebnisse;
Wir stellen nun die JSON-Antwort vor. Sie wird von der folgenden Klasse [JsonResponse] generiert:
Dieser Code ist uns bekannt, da wir ihm schon oft begegnet sind. Es handelt sich um den Code für die Funktion [json_response] im Modul [myutils].
30.9. Erste Tests
In dem von uns untersuchten Code sind wir auf drei Statuscodes gestoßen:
- 700: /init-session erfolgreich;
- 701: /init-session fehlgeschlagen;
- 101: Ungültige Anfrage, entweder weil die Sitzung nicht initialisiert wurde oder weil der Benutzer nicht authentifiziert ist;
Wir werden versuchen, diese mit einer JSON-Sitzung auszulösen.
- Wir starten den Webserver, das DBMS und den Mailserver;
- Wir starten einen Postman-Client;
Test 1
Zunächst demonstrieren wir eine ungültige Anfrage, da die Sitzung noch nicht initialisiert wurde:

- [1-2]: Die Anfrage [POSThttp://localhost:5000/authentifier-utilisateur] ist eine gültige Route:
# authenticate-user
@app.route('/authentifier-utilisateur', methods=['POST'])
def authentifier_utilisateur() -> tuple:
# execute the controller associated with the action
return front_controller()
Dies wird jedoch nur akzeptiert, wenn die Sitzung zuvor mit der Aktion [/init-session] initialisiert wurde.
Führen wir die Anfrage aus und sehen wir uns das vom Server gesendete Ergebnis an:

- [1-2]: Wir haben eine JSON-Antwort erhalten. Wenn der Antworttyp vom Client noch nicht angegeben wurde, verwendet der Server JSON für die Antwort;
- [3-5]: das JSON-Dictionary der Antwort;
- [action]: die ausgeführte Aktion;
- [status]: der Antwortstatuscode. Ein Code [x01] weist auf einen Fehler hin;
- [response]: ist auf jede Aktion zugeschnitten. Hier enthält er eine Fehlermeldung;
Initialisieren wir nun eine Sitzung mit einem falschen Antworttyp:

- [1-2] ist eine gültige Route:
# init-session
@app.route('/init-session/<string:type_response>', methods=['GET'])
def init_session(type_response: str) -> tuple:
# execute the controller associated with the action
return front_controller()
Dadurch gelangt die Anfrage in die Verarbeitungs-Pipeline des MVC-Servers. Sie sollte jedoch während dieser Verarbeitung abgelehnt werden, da der angeforderte Sitzungstyp falsch ist.
Die Antwort lautet wie folgt:

- in [4] ein Fehlercode [x01];
- in [5] die Fehlerbeschreibung;
Nun initialisieren wir eine JSON-Sitzung:

Die Antwort lautet wie folgt:

Initialisieren wir nun eine XML-Sitzung. Die JSON-Antwort wird durch eine XML-Antwort ersetzt, die von der folgenden [XmlResponse]-Klasse generiert wird:
Das ist uns bekannter Code – er stammt aus der Funktion [xml_response] im gemeinsam genutzten Modul [myutils].
Wir initialisieren eine XML-Sitzung:

Die Antwort des Servers lautet dann wie folgt:

Wir erhalten dieselbe Antwort wie in JSON, doch diesmal ist die Antwort als XML formatiert.
30.10. Die Aktion [authenticate-user]
Die Aktion [authenticate-user] ermöglicht es Ihnen, einen Benutzer zu authentifizieren, der die Steuerberechnungsanwendung nutzen möchte. Ihre Route ist im Skript [main] wie folgt definiert:
Der Server erwartet zwei POST-Parameter:
- [user]: die ID des Benutzers;
- [password]: das Passwort des Benutzers;
Die Liste der autorisierten Benutzer ist in der Konfiguration [config] definiert:
# utilisateurs autorisés à utiliser l'application
"users": [
{
"login": "admin",
"password": "admin"
}
],
Hier haben wir eine Liste mit einem einzigen Element.
Die Aktion [authenticate-user] wird vom folgenden Controller [AuthentifierUtilisateurController] verarbeitet:
- Zeile 14: Abrufen der POST-Parameter;
- Zeile 19: die Liste der in der Anfrage gefundenen Fehler;
- Zeilen 20–24: Wir überprüfen, ob tatsächlich zwei Parameter gesendet wurden;
- Zeilen 27–31: Prüfen, ob ein [users]-Parameter vorhanden ist;
- Zeilen 32–36: Prüfen, ob ein [password]-Parameter vorhanden ist;
- Zeilen 38–39: Wenn die übermittelten Parameter falsch sind, bereite eine HTTP-400-BAD-REQUEST-Antwort vor;
- Zeilen 40–58: Überprüfen, ob die Anmeldedaten [user, password] zu einem Benutzer gehören, der zur Nutzung der Anwendung berechtigt ist;
- Zeilen 51–55: Wenn der Benutzer (user, password) nicht zur Nutzung der Anwendung berechtigt ist, bereite eine HTTP-401-UNAUTHORIZED-Antwort vor;
- Zeilen 56–58: Wenn der Benutzer autorisiert ist, speichern wir in der Sitzung unter dem Schlüssel [user], dass er sich authentifiziert hat;
Beachten Sie: Wenn der Benutzer mit den Anmeldedaten [Anmeldedaten1] authentifiziert wurde und die Authentifizierung mit den Anmeldedaten [Anmeldedaten2] fehlschlägt, bleibt er mit den Anmeldedaten [Anmeldedaten1] authentifiziert.
Führen wir einige Postman-Tests durch:
- Wir starten den Webserver, das DBMS und den Mailserver;
- Verwenden des Postman-Clients:
- starten wir eine JSON-Sitzung;
- dann authentifizieren;
Hier sind verschiedene Szenarien.
Fall 1: POST ohne übermittelte Parameter

- In [3-5] hat der POST-Request keinen Body;
Das Ergebnis der Anfrage ist wie folgt:

- In [2] erhielten wir eine HTTP-400-BAD-REQUEST-Antwort;
- In [5] erhielten wir den Fehlercode [201];
Fall 2: POST mit falschen Anmeldedaten

- In [6] sind die Anmeldedaten falsch;
Der Server sendet die folgende Antwort:

- in [2] die HTTP-Antwort 401 UNAUTHORIZED;
- In [5] die Fehlerantwort;
Fall 2: POST mit korrekten Anmeldedaten

- In [6] sind die Anmeldedaten korrekt;
Die Antwort des Servers lautet wie folgt:
- in [2] eine HTTP-200-OK-Antwort;
- in [5] die Erfolgsmeldung;
30.11. Die Aktion [calculate_tax]
Die Aktion [calculate_tax] berechnet die Steuer eines Steuerzahlers. Ihre Route ist im Skript [main] wie folgt definiert:
Der Server erwartet drei POST-Parameter:
- [married]: ja / nein;
- [children]: Anzahl der Kinder des Steuerpflichtigen;
- [salary]: Jahresgehalt des Steuerpflichtigen;
Der Controller [CalculateTaxController] verarbeitet die Aktion [calculate_tax]:
- Zeile 13: Wir rufen den Namen der aktuellen Aktion ab;
- Zeile 17: Wir sammeln die Fehler in einer Liste;
- Zeile 19: Die übermittelten Parameter abrufen. Diese werden im Format [x-www-form-urlencoded] übermittelt, weshalb wir sie aus [request.form] abrufen. Wären sie als JSON übermittelt worden, hätten wir sie aus [request.data] abgerufen;
- Zeilen 21–24: Wir überprüfen, ob tatsächlich drei übermittelte Parameter vorhanden sind;
- Zeilen 27–36: Wir prüfen auf das Vorhandensein und die Gültigkeit des übermittelten Parameters [married];
- Zeilen 37–48: Überprüfung auf das Vorhandensein und die Gültigkeit des übermittelten Parameters [children];
- Zeilen 49–60: Überprüfung auf Vorhandensein und Gültigkeit des gesendeten Parameters [salary];
- Zeilen 62–66: Wenn ein Fehler aufgetreten ist, wird eine 400 BAD REQUEST-Fehlerantwort mit dem Statuscode [301] gesendet;
- Zeilen 69–71: Wenn kein Fehler aufgetreten ist, Vorbereitung der Steuerberechnung. Dazu
- Zeile 70: Abruf einer Referenz aus der [business]-Schicht;
- Zeile 71: Abrufen von Daten von der Steuerbehörde in der Serverkonfiguration;
- Zeilen 72–74: Die Steuer des Steuerpflichtigen wird berechnet;
- Zeilen 75–77: Wir zählen die Anzahl der vom Benutzer durchgeführten Steuerberechnungen;
- Zeile 76: Rufen Sie die Nummer der zuletzt durchgeführten Berechnung aus der Sitzung ab. Hier bezeichnen wir das Ergebnis einer Berechnung als [Simulation];
- Zeile 77: Die Nummer der letzten Simulation wird erhöht;
- Zeile 78: Diese Nummer wird in der Sitzung gespeichert;
- Zeilen 79–84: Um die vom Benutzer durchgeführten Berechnungen nachzuverfolgen, speichern wir die Liste der von ihm durchgeführten Simulationen in seiner Sitzung;
- Zeile 80: Eine Simulation ist das Wörterbuch eines TaxPayer-Objekts, dessen [id]-Eigenschaft den Wert der Simulationsnummer hat;
- Zeilen 82–84: Die aktuelle Simulation wird zur Liste der Simulationen in der Sitzung hinzugefügt;
- Zeilen 86–87: Wir bereiten eine erfolgreiche HTTP-Antwort vor;
- Zeile 90: Wir geben das Ergebnis zurück;
Führen wir einige Tests durch: Der Webserver, das DBMS, der Mailserver und ein Postman-Client werden gestartet.
Fall 1: Durchführung einer Steuerberechnung, während die Sitzung nicht initialisiert ist

Die Antwort lautet wie folgt:

Fall 2: Durchführung einer Steuerberechnung ohne Authentifizierung
Zunächst starten wir eine JSON-Sitzung mit [/init-session/json]. Dann stellen wir dieselbe Anfrage wie zuvor. Die Antwort lautet wie folgt:

Fall 3: Durchführung einer Steuerberechnung mit fehlenden Parametern
Wir initialisieren eine JSON-Sitzung, authentifizieren uns und stellen dann die folgende Anfrage:

- In [5] fehlt der Parameter [married];
Die Antwort lautet wie folgt:
Fall 4: Steuerberechnung mit falschen Parametern


Die Antwort des Servers lautet wie folgt:

Fall 4: Durchführung einer Steuerberechnung mit korrekten Parametern

Die Antwort des Servers lautet wie folgt:

30.12. Die Aktion [list-simulations]
Die Aktion [list-simulations] ermöglicht es einem Benutzer, die Liste der Simulationen anzuzeigen, die er seit Beginn der Sitzung durchgeführt hat. Ihre Route ist im Skript [main] wie folgt definiert:
Der Server erwartet keine Parameter. Die Aktion [lister-simulations] wird vom folgenden [ListerSimulationsController] verarbeitet:
- Zeile 13: Die Liste der Simulationen wird aus der Sitzung abgerufen;
- Zeilen 15–16: Eine Erfolgsmeldung wird zurückgegeben;
Führen wir den folgenden Postman-Test aus:
- Wir starten eine JSON-Sitzung;
- Wir authentifizieren uns;
- Führen zwei Steuerberechnungen durch;
- Wir fordern die Liste der Simulationen an;
Die Anfrage lautet wie folgt:
- In [3] gibt es keine Parameter;
Die Antwort des Servers lautet wie folgt:

- in [4] die Liste der Simulationen des Benutzers;
30.13. Die Aktion [delete-simulation]
Die Aktion [delete-simulation] ermöglicht es einem Benutzer, eine der Simulationen aus seiner Simulationsliste zu löschen. Ihre Route ist im Skript [main] wie folgt definiert:
Der Server erwartet einen einzigen Parameter: die Nummer der zu löschenden Simulation. Die Aktion [delete-simulation] wird vom folgenden [DeleteSimulationController] verarbeitet:
- Zeile 10: Abrufen der beiden Elemente des Anfragepfads. Sie werden als Zeichenfolgen abgerufen;
- Zeile 13: Der Parameter [number] wird in eine Ganzzahl umgewandelt. Dass dies möglich ist, wissen wir aufgrund der Signatur der Route,
@app.route('/supprimer-simulation/<int:numero>', methods=['GET'])
Wir wissen auch, dass es sich um eine ganze Zahl >=0 handelt. Tatsächlich kann es keine URL wie [/delete-simulation/-4] geben. Diese wird vom Flask-Server abgelehnt;
- Zeile 15: Wir rufen die Liste der Simulationen aus der Sitzung ab;
- Zeile 16: Mit der Funktion [filter] suchen wir nach der Simulation mit id==number. Wir erhalten ein [filter]-Objekt, das wir in eine [list] umwandeln;
- Zeilen 17–20: Wenn der Filter nichts zurückgibt, existiert die zu löschende Simulation nicht. Wir geben eine Fehlermeldung zurück, die darauf hinweist;
- Zeilen 21–23: Wir löschen die vom Filter zurückgegebene Simulation;
- Zeile 25: Wir stellen die neue Liste der Simulationen in der Sitzung wieder her;
- Zeile 27: Wir geben die neue Liste der Simulationen in der Antwort zurück;
Wir führen einen Erfolgstest und einen Fehlertest durch. Wir führen Simulationen durch und fordern anschließend die Liste der Simulationen an:

- Die Simulationen hier haben die Nummern 2 und 3;
Wir fordern an, dass die Simulation mit der Nummer 3 entfernt wird.

Die Antwort lautet wie folgt:
Wiederholen wir nun denselben Vorgang (Löschen der Simulation mit der ID 3). Die Antwort lautet dann wie folgt:


30.14. Die Aktion [end-session]
Die Aktion [end-session] ermöglicht es einem Benutzer, seine Simulationssitzung zu beenden. Ihr Pfad ist im Skript [main] wie folgt definiert:
Der Server erwartet keine Parameter. Die Aktion wird vom folgenden [FinSessionController] verarbeitet:
- Zeile 13: Alle Schlüssel aus der Sitzung löschen. Dies löscht:
- [typeResponse]: den Typ der HTTP-Antworten (json, xml, html);
- [simulation_id]: die ID der zuletzt durchgeführten Simulation;
- [simulations]: die Liste der Simulationen des Benutzers;
- [user]: die Kennung, dass der Benutzer authentifiziert wurde;
- die Antwort zurückgeben;
Man könnte sich fragen, wie die HTTP-Antwort aus Zeile 15 zurückgegeben wird, da der Antworttyp nun nicht mehr in der Sitzung vorhanden ist. Um dies herauszufinden, müssen wir zur Funktion |front_controller| im Hauptskript [main] zurückkehren und sie wie folgt ändern:
…
# on not# note the type of response required if this information is in the session
type_response1 = session.get('typeResponse', None)
# forward the request to the main controller
main_controller = config['controllers']["main-controller"]
résultat, status_code = main_controller.execute(request, session, config)
# we log the result sent to the customer
log = f"[front_controller] {résultat}\n"
logger.write(log)
# was there a fatal error?
if status_code == status.HTTP_500_INTERNAL_SERVER_ERROR:
# send an e-mail to the application administrator
send_adminmail(config, log)
# determine the desired type of response
type_response2=session.get('typeResponse')
if type_response2 is None and type_response1 is None:
# the session type has not yet been set - it will be jSON
type_response = 'json'
elif type_response2 is not None:
# the type of response is known and in the session
type_response = type_response2
else:
type_response=type_response1
# build the response to be sent
response_builder = config["responses"][type_response]
response, status_code = response_builder \
.build_http_response(request, session, config, status_code, résultat)
# we send the answer
return response, status_code
- Zeile 3: Der Typ der aktuell in der Sitzung befindlichen Antwort wird gespeichert;
- Zeile 6: Die Aktion wird ausgeführt. Wenn es sich um
- [end-session], ist der Schlüssel [typeResponse] nicht mehr in der Sitzung vorhanden;
- [init-session], hat der Schlüssel [typeResponse] in der Sitzung möglicherweise seinen Wert geändert;
- Zeilen 14–20: Die HTTP-Antwort muss gesendet werden. Wir müssen wissen, in welcher Form:
- Zeilen 16–18: Wenn der Antworttyp weder durch [type_response1] in Zeile 3 noch durch [type_response2] in Zeile 15 definiert ist, wurde der Antworttyp weder vor noch nach der Aktion definiert. Wir verwenden dann JSON (Zeile 18);
- Zeilen 19–21: Wenn [type_response2] vorhanden ist – der Antworttyp in der Sitzung nach der Aktion –, dann ist dies der zu verwendende Typ;
- Zeilen 22–23: Andernfalls ist [type_response1], der Antworttyp vor der Aktion (der [end-session] sein muss), der zu verwendende Typ;
30.15. Die Aktion [get-admindata]
Wir werden nun die beiden URLs besprechen, die für die JSON- und XML-Dienste reserviert sind:
Aktion | Rolle | Ausführungskontext |
/get-admindata | Gibt die Steuerdaten zurück, die zur Berechnung der Steuer verwendet werden | . Wird nur verwendet, wenn der Sitzungstyp „json“ oder „xml“ ist. Der Benutzer muss authentifiziert sein |
/calculate-taxes | Berechnet die Steuer für eine Liste von Steuerzahlern, die im JSON-Format übermittelt wurde | GET-Anfrage. Wird nur verwendet, wenn der Sitzungstyp „json“ oder „xml“ ist. Der Benutzer muss authentifiziert sein |
Die URL [/get-admindata] ist in den Routen des Hauptskripts [main] wie folgt definiert:
Die Route [/get-admindata] wird vom folgenden [GetAdminDataController] verarbeitet:
- Zeilen 13–21: Wir prüfen, ob wir uns in einer JSON- oder XML-Sitzung befinden;
- Zeile 24: Das Datenwörterbuch der Steuerverwaltung, das beim Start des Servers in die Konfiguration aufgenommen wurde, wird zurückgegeben:
# admindata sera une donnée de portée application en lecture seule
config["admindata"] = config["layers"]["dao"].get_admindata()
Verwenden wir einen Postman-Client und senden wir eine Anfrage an die URL [/get-admindata], nachdem wir eine JSON-Sitzung gestartet und uns authentifiziert haben:

Die Serverantwort lautet wie folgt:

30.16. Die Aktion [calculate-taxes]
Die Aktion [calculate-taxes] berechnet die Steuern für eine Liste von Steuerzahlern, die im Request-Body als JSON-String enthalten ist. Diese Aktion ist uns bereits bekannt: In der vorherigen Version hieß sie [calculate_tax_in_bulk_mode].
Ihre Route lautet wie folgt:
Diese Aktion wird vom folgenden [CalculateTaxesController] verarbeitet:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | |
- Zeilen 16–24: Wir überprüfen, ob wir uns tatsächlich in einer JSON- oder XML-Sitzung befinden
- Zeilen 26–120: Dieser Code ist uns im Allgemeinen bekannt. Er stammt aus der Funktion |index_controller| in Version 10 der Anwendung, die an die Spezifikationen der implementierten Schnittstelle [InterfaceController] angepasst wurde;
- Zeilen 104–115: Code, der hinzugefügt wurde, um der neuen Umgebung dieses Controllers Rechnung zu tragen. Wir haben soeben Steuerberechnungen durchgeführt. Wir müssen die Ergebnisse in der Liste der Simulationen speichern, die in der Sitzung verwaltet wird;
- Zeile 105: Wir rufen die Liste der Simulationen in der Sitzung ab;
- Zeile 106: Wir rufen die Nummer der zuletzt durchgeführten Simulation ab;
- Zeilen 107–112: Wir durchlaufen die Liste der Wörterbücher, die die Ergebnisse der Steuerberechnung enthalten; wir weisen jedem eine Simulations-ID zu, und jedes Wörterbuch wird der Liste der Simulationen hinzugefügt;
- Zeilen 113–115: Die neue Liste der Simulationen und die Nummer der zuletzt durchgeführten Simulation werden an die Sitzung zurückgegeben;
Nach der Initialisierung einer JSON-Sitzung und der Authentifizierung führen wir den folgenden Postman-Test durch:


Die Serverantwort lautet wie folgt:

Wenn wir nun die Liste der Simulationen abfragen:
Beachten Sie, dass in der Ergebnisliste für [/calcul-impots] Steuerzahler kein [id]-Attribut haben, während in der Liste der Simulationen jede Simulation eine Nummer hat, die sie identifiziert.




