32. HTML-Modus in Version 12
Zu Beginn von Version 12 haben wir erwähnt, dass wir die Anwendung in mehreren Phasen entwickeln würden. Wir schrieben:
- Basierend auf den Ansichten der HTML-Anwendung werden wir die Aktionen definieren, die die Webanwendung implementieren muss. Wir werden hier die tatsächlichen Ansichten verwenden, aber es könnten auch einfach Ansichten auf Papier sein;
- Auf der Grundlage dieser Aktionen werden wir die Service-URLs für die HTML-Anwendung definieren;
- Wir werden diese Service-URLs mithilfe eines Servers implementieren, der JSON bereitstellt. Dies ermöglicht es uns, das Framework des Webservers zu 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;
Wir verfügen über einen betriebsbereiten JSON- und XML-Server. Nun können wir uns dem HTML-Server zuwenden. Wir werden sehen, dass dieser die gesamte für den JSON/XML-Server entwickelte Architektur wiederverwendet und um die Verwaltung von HTML-Ansichten erweitert.
32.1. MVC-Architektur
Wir werden das MVC-Architekturmuster (Model–View–Controller) wie folgt implementieren:

Die Verarbeitung einer Client-Anfrage verläuft wie folgt:
- 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] in 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 [Hauptcontroller] ü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 [Haupt-Controller] [3a] den entsprechenden Antworttyp und weist ihn an, die Antwort an den Client zu senden. Der [Haupt-Controller] übergibt ihm sowohl die Antwort als auch den Statuscode, der vom ausgeführten [Controller/Action] bereitgestellt wurde;
- Wenn die gewünschte Antwort vom Typ JSON oder XML ist, formatiert die ausgewählte Antwort die Antwort vom [Controller/Action], die ihr bereitgestellt 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 bereitgestellten Statuscodes eine der HTML-Ansichten [Vuei] aus. Dies ist das V in MVC. Eine einzelne Ansicht entspricht einem 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;
32.2. Die Verzeichnisstruktur der HTML-Serverskripte

- in [1], die statischen Elemente des HTML-Servers;
- in [2-3], die HTML-Server-Ansichten V. Die Fragmente [2] sind wiederverwendbare Elemente innerhalb der Ansichten [3];
- in [4], ein Ordner, der zum statischen Testen von Ansichten verwendet wird;
- in [5], der Ordner für die M-Modelle der V-Ansichten, das M in MVC;
32.3. Übersicht über die Ansichten
Die HTML-Webanwendung verwendet vier Ansichten. Die erste Ansicht ist die Authentifizierungsansicht:
- Die Aktion, die zu dieser ersten Ansicht führt, ist die Aktion [/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 diese Ansicht aufruft;
- in [2] löst ein Klick auf die Schaltfläche [Validate] die Ausführung der Aktion [/calculate-tax] mit drei übermittelten Parametern [2-5] aus;
- 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 [1] die Aktion [/list-simulations], die diese Ansicht aufruft;
- in [2] löst das Klicken auf den Link [Löschen] die Aktion [/delete-simulation] mit einem Parameter aus, nämlich der Nummer der aus der Liste zu löschenden Simulation;
- 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;
Die vierte Ansicht wird als Ansicht für unerwartete Fehler bezeichnet:
- in [1]: Der Benutzer hat die URL selbst eingegeben. In diesem Beispiel gab es jedoch keine Simulationen. Wir erhalten daher die Fehlermeldung [2]. Diese Meldung ist uns bekannt. Wir hatten sie bereits in JSON/XML. Wir bezeichnen diese Art von Fehler als „unerwarteten Fehler“, da er bei normaler Nutzung der Anwendung nicht auftreten kann. Er kann nur auftreten, wenn der Benutzer die URLs selbst eingibt;
- Im Falle eines unerwarteten Fehlers ermöglichen Ihnen die Links [3–5] die Rückkehr zu einer der anderen drei Ansichten;
Sehen wir uns die verschiedenen Service-URLs für den JSON/XML-Server an:
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 ausgestellt werden, wenn der Sitzungstyp (json, xml, html) bekannt ist und der Benutzer authentifiziert ist |
Diese verschiedenen Service-URLs werden auch für den HTML-Server verwendet.
32.4. Konfigurieren von Views
Eine Aktion wird von einem Controller verarbeitet. Dieser Controller gibt ein Tupel (result, status_code) zurück, wobei:
- [result] ein Wörterbuch mit den Schlüsseln [action, status, response] ist;
- [status_code] der Statuscode der HTTP-Antwort ist, die an den Client gesendet wird;
In einer HTML-Sitzung hängt die nach einer Aktion angezeigte Seite vom vom Controller zurückgegebenen Statuscode ab. Diese Abhängigkeit spiegelt sich in der [config]-Konfiguration wie folgt wider:
# les vues HTML et leurs modèles dépendent de l'état rendu par le contrôleur
"views": [
{
# vue d'authentification
"états": [
# /init-session réussite
700,
# /authentifier-utilisateur échec
201
],
"view_name": "views/vue-authentification.html",
"model_for_view": ModelForAuthentificationView()
},
{
# vue du calcul de l'impôt
"états": [
# /authentifier-utilisateur réussite
200,
# /calculer-impot réussite
300,
# /calculer-impot échec
301,
# /afficher-calcul-impot
800
],
"view_name": "views/vue-calcul-impot.html",
"model_for_view": ModelForCalculImpotView()
},
{
# vue de la liste des simulations
"états": [
# /lister-simulations
500,
# /supprimer-simulation
600
],
"view_name": "views/vue-liste-simulations.html",
"model_for_view": ModelForListeSimulationsView()
}
],
# vue des erreurs inattendues
"view-erreurs": {
"view_name": "views/vue-erreurs.html",
"model_for_view": ModelForErreursView()
},
# redirections
"redirections": [
{
"états": [
400, # /fin-session réussite
],
# redirection vers
"to": "/init-session/html",
}
],
}
- Zeilen 2–40: [views] ist eine Liste von Ansichten. Betrachten wir die Ansicht in den Zeilen 3–13:
- Zeile 11: die angezeigte Ansicht V;
- Zeile 12: die Klasseninstanz, die für die Generierung des Modells M für diese Ansicht zuständig ist;
- Zeilen 5–10: die Zustände, die zu dieser Ansicht führen;
- Zeilen 3–13: die Authentifizierungsansicht;
- Zeilen 14–28: die Steuerberechnungsansicht;
- Zeilen 29–39: die Simulationslistenansicht;
- Zeilen 42–46: die Ansicht für unerwartete Fehler;
- Zeilen 49–57: Einige Status führen über eine Weiterleitung zu einer Ansicht. Dies ist der Fall bei Status 400, der der erfolgreichen Aktion [/fin-session] entspricht. Der Client muss dann zur Aktion [http://machine:port/chemin/init-session/html] weitergeleitet werden;
Wir stellen nun die verschiedenen Ansichten vor.
32.5. Die Authentifizierungsansicht

32.5.1. Ansichtsübersicht
Die Authentifizierungsansicht sieht wie folgt aus:

Die Ansicht besteht aus zwei Elementen, die wir als Fragmente bezeichnen:
- Fragment [1] wird durch das Fragment [v-banner.html] generiert;
- Fragment [2] wird durch das Fragment [v-authentication.html] generiert;
Die Authentifizierungsansicht wird durch die folgende Seite [vue-authentification.html] generiert:
<!-- document HTML -->
<!doctype html>
<html lang="fr">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
<title>Application impôts</title>
</head>
<body>
<div class="container">
<!-- headband -->
{% include "fragments/v-bandeau.html" %}
<!-- two-column line -->
<div class="row">
<div class="col-md-9">
{% include "fragments/v-authentification.html" %}
</div>
</div>
<!-- if error - displays an error alert -->
{% if modèle.error %}
<div class="row">
<div class="col-md-9">
<div class="alert alert-danger" role="alert">
Les erreurs suivantes se sont produites :
<ul>{{modèle.erreurs|safe}}</ul>
</div>
</div>
</div>
{% endif %}
</div>
</body>
</html>
Kommentare
- Zeile 2: Ein HTML-Dokument beginnt mit dieser Zeile;
- Zeilen 3–36: Die HTML-Seite wird von den Tags <html> und </html> umschlossen;
- Zeilen 4–11: der Kopf (head) des HTML-Dokuments;
- Zeile 6: Der Tag <meta charset> gibt hier an, dass das Dokument in UTF-8 kodiert ist;
- Zeile 7: Der Tag <meta name='viewport'> legt die anfängliche Ansicht des Viewports fest: über die gesamte Breite des anzeigenden Bildschirms (width) in seiner ursprünglichen Größe (initial-scale), ohne die Größe an einen kleineren Bildschirm anzupassen (shrink-to-fit);
- Zeile 9: Der Tag <link rel='stylesheet'> gibt die CSS-Datei an, die das Erscheinungsbild des Viewports bestimmt. Hier verwenden wir das CSS-Framework Bootstrap 4.4.1 [https://getbootstrap.com/docs/4.0/getting-started/introduction/] ;
- Zeile 10: Das title-Tag legt den Seitentitel fest:

- Zeilen 13–35: Der Hauptteil der Webseite wird von den Tags `body` und `/body` umschlossen;
- Zeilen 14–34: Das <div>-Tag begrenzt einen Abschnitt der angezeigten Seite. Die im View verwendeten [class]-Attribute beziehen sich alle auf das Bootstrap-CSS-Framework. Das <div class=’container’>-Tag (Zeile 14) begrenzt einen Bootstrap-Container;
- Zeile 26: Das Fragment [v-banner.html] wird eingebunden. Dieses Fragment generiert das Banner der Seite [1]. Wir werden es in Kürze beschreiben;
- Zeilen 18–22: Das <div class=’row’>-Tag definiert eine Bootstrap-Zeile. Diese Zeilen bestehen aus 12 Spalten;
- Zeile 19: Das -Tag definiert einen Abschnitt mit 9 Spalten;
- Zeile 20: Wir binden das Fragment [v-authentification.html] ein, das das Authentifizierungsformular der Seite anzeigt [2]. Wir werden es in Kürze beschreiben;
- Zeilen 24–33: Der HTML-Code in diesen Zeilen wird nur verwendet, wenn [model.error] True ist. Wir werden immer so vorgehen: Das Modell für eine HTML-Ansicht wird in einem Wörterbuch [model] gekapselt;
- Zeilen 24–33: Die Authentifizierung schlägt fehl, wenn der Benutzer falsche Anmeldedaten eingibt. In diesem Fall wird die Authentifizierungsansicht mit einer Fehlermeldung erneut angezeigt. Das Attribut [model.error] gibt an, ob diese Fehlermeldung angezeigt werden soll;
- Zeilen 27–30: Definieren Sie einen Bereich mit rosa Hintergrund (class="alert alert-danger") (Zeile 27);

- Zeile 28: etwas Text;
- Zeile 29: Der HTML-Tag <ul> (ungeordnete Liste) zeigt eine Liste mit Aufzählungszeichen an. Jedes Listenelement muss die Syntax <li>element</li> haben. Hier zeigen wir den Wert von [model.errors] an. Dieser Wert wird durch den Filter [safe] gefiltert (auf das Vorhandensein von |). Standardmäßig „escapet“ Flask alle HTML-Tags, die eine an den Browser gesendete Zeichenkette enthalten könnte, damit der Browser sie nicht interpretiert. Manchmal möchten wir jedoch, dass sie interpretiert werden. Dies ist hier der Fall, da die Zeichenkette [model.errors] die HTML-Tags <li> und </li> enthält, die zur Abgrenzung eines Listenelements dienen. In diesem Fall verwenden wir den [safe]-Filter, der Flask mitteilt, dass die anzuzeigende Zeichenkette sicher ist und dass daher keine gefundenen HTML-Tags bereinigt werden sollen;
Beachten wir die dynamischen Elemente, die in diesem Code definiert werden müssen:
- [model.error]: zur Anzeige einer Fehlermeldung;
- [model.errors]: eine Liste (im HTML-Sinne) von Fehlermeldungen;
32.5.2. Das Fragment [v-banner.html]
Das Fragment [v-banner.html] zeigt das obere Banner aller Ansichten in der Webanwendung an:

Der Code für das Fragment [v-banner.html] lautet wie folgt:
<!-- Bootstrap Jumbotron -->
<div class="jumbotron">
<div class="row">
<div class="col-md-4">
<img src="{{ url_for('static', filename='images/logo.jpg') }}" alt="Cerisier en fleurs"/>
</div>
<div class="col-md-8">
<h1>
Calculez votre impôt
</h1>
</div>
</div>
</div>
Kommentare
- Zeilen 2–13: Das Banner ist in einen Bootstrap-Jumbotron-Abschnitt [<div class="jumbotron">] eingebettet. Diese Bootstrap-Klasse gestaltet den angezeigten Inhalt auf eine bestimmte Weise, um ihn hervorzuheben;
- Zeilen 3–12: eine Bootstrap-Zeile;
- Zeilen 4–6: Ein Bild [img] wird in den ersten vier Spalten der Zeile platziert;
- Zeile 5: Die Syntax:
verwendet die Flask-Funktion [url_for]. Hier ist ihr Wert die URL der Datei [images/logo.jpg] im Ordner [static];
- Zeilen 7–11: Die anderen 8 Spalten der Zeile (denken Sie daran, dass es insgesamt 12 sind) werden verwendet, um Text (Zeile 9) in großer Schriftgröße (<h1>, Zeilen 8–10) anzuzeigen;
32.5.3. Das Fragment [v-authentification.html]
Das Fragment [v-authentification.html] zeigt das Authentifizierungsformular der Webanwendung an:

Der Code für das Fragment [v-authentification.html] lautet wie folgt:
<!-- form HTML - post its values with the [authenticate-user] action -->
<form method="post" action="/authentifier-utilisateur">
<!-- title -->
<div class="alert alert-primary" role="alert">
<h4>Veuillez vous authentifier</h4>
</div>
<!-- bootstrap form -->
<fieldset class="form-group">
<!-- 1st line -->
<div class="form-group row">
<!-- wording -->
<label for="user" class="col-md-3 col-form-label">Nom d'utilisateur</label>
<div class="col-md-4">
<!-- text input field -->
<input type="text" class="form-control" id="user" name="user"
placeholder="Nom d'utilisateur" value="{{ modèle.login }}" required>
</div>
</div>
<!-- 2nd line -->
<div class="form-group row">
<!-- wording -->
<label for="password" class="col-md-3 col-form-label">Mot de passe</label>
<!-- text input field -->
<div class="col-md-4">
<input type="password" class="form-control" id="password" name="password"
placeholder="Mot de passe" required>
</div>
</div>
<!-- submit] button on a 3rd line -->
<div class="form-group row">
<div class="col-md-2">
<button type="submit" class="btn btn-primary">Valider</button>
</div>
</div>
</fieldset>
</form>
Kommentare
- Zeilen 2–39: Das <form>-Tag definiert ein HTML-Formular. Dieses Formular weist im Allgemeinen folgende Merkmale auf:
- Es definiert Eingabefelder (Tags <input> in den Zeilen 17 und 27);
- es verfügt über eine [submit]-Schaltfläche (Zeile 34), die die eingegebenen Werte an die im [action]-Attribut des [form]-Tags (Zeile 2) angegebene URL sendet. Die zur Anforderung dieser URL verwendete HTTP-Methode ist im [method]-Attribut des [form]-Tags (Zeile 2) festgelegt;
- Wenn der Benutzer hier auf die Schaltfläche [Submit] klickt (Zeile 34), sendet der Browser die im Formular eingegebenen Werte per POST (Zeile 2) an die URL [/authenticate-user] (Zeile 2);
- Die gesendeten Werte sind die Werte, die der Benutzer in die Eingabefelder in den Zeilen 17 und 27 eingegeben hat. Sie werden im Hauptteil der vom Browser gesendeten HTTP-Anfrage im Format [x-www-form-urlencoded] übermittelt. Die Parameternamen [user, password] entsprechen den [name]-Attributen der Eingabefelder in den Zeilen 17 und 27;
- Zeilen 5–7: Ein Bootstrap-Abschnitt zur Anzeige eines Titels auf blauem Hintergrund:
- Zeilen 10–37: ein Bootstrap-Formular. Alle Formularelemente werden dann auf eine bestimmte Weise gestaltet;
- Zeilen 12–20: Definieren die erste Bootstrap-Zeile des Formulars:
![]()
- Zeile 14 definiert das Label [1] über drei Spalten. Das [for]-Attribut des [label]-Tags verknüpft das Label mit dem [id]-Attribut des Eingabefelds in Zeile 17;
- Zeilen 15–19: Platzieren Sie das Eingabefeld in einem vier Spalten umfassenden Layout;
- Zeilen 17–18: Das HTML-Tag [input] definiert ein Eingabefeld. Es hat mehrere Attribute:
- [type='text']: Dies ist ein Texteingabefeld. Sie können beliebige Zeichen eingeben;
- [class='form-control']: Bootstrap-Stil für das Eingabefeld;
- [id='user']: Kennung für das Eingabefeld. Diese Kennung wird in der Regel von CSS- und JavaScript-Code verwendet;
- [name='user']: Der Name des Eingabefelds. Der vom Benutzer eingegebene Wert wird vom Browser unter diesem Namen [user=xx] übermittelt;
- [placeholder='prompt']: der Text, der im Eingabefeld angezeigt wird, wenn der Benutzer noch nichts eingegeben hat;
![]()
- (Fortsetzung)
- [value='value']: Der Text 'value' wird im Eingabefeld angezeigt, sobald es erscheint, noch bevor der Benutzer etwas eingibt. Dieser Mechanismus wird im Fehlerfall verwendet, um die Eingabe anzuzeigen, die den Fehler verursacht hat. Hier ist dieser Wert der Wert der Variablen [model.login];
- [required]: verlangt vom Benutzer die Eingabe eines Werts, damit das Formular an den Server gesendet werden kann:
- Zeilen 21–30: ähnlicher Code für das Passwort-Eingabefeld;
- Zeile 27: [type='password'] erstellt ein Texteingabefeld (Sie können alles eingeben), aber die eingegebenen Zeichen werden ausgeblendet:
![]()
- Zeilen 32–36: eine dritte Bootstrap-Zeile für die Schaltfläche [Submit];
- Zeile 34: Da diese Schaltfläche das Attribut [type="submit"] enthält, veranlasst ein Klick darauf den Browser, die eingegebenen Werte an den Server zu senden, wie zuvor erläutert. Das CSS-Attribut [class="btn btn-primary"] zeigt eine blaue Schaltfläche an:
Es gibt noch eine letzte Sache zu erklären. In Zeile 2 definiert das Attribut [action="/authentifier-utilisateur"] eine unvollständige URL (sie beginnt nicht mit http://machine:port/chemin). In unserem Beispiel haben alle Anwendungs-URLs die Form [http://machine:port/chemin/action/param1/param2/..], wobei [http://machine:port/chemin] die Wurzel der Service-URLs ist. In [action="/authenticate-user"] haben wir eine absolute URL, d. h. eine, die von der Wurzel der URLs aus gemessen wird. Die vollständige URL für die POST-Anfrage lautet daher [http://machine:port/chemin/authentifier-utilisateur], und diese wird vom Browser verwendet.
Beachten Sie, dass dieses Fragment die Vorlage [model.login] verwendet.
32.5.4. Visuelle Tests
Wir können die Ansichten bereits vor ihrer Integration in die Anwendung testen. Das Ziel hierbei ist es, ihr visuelles Erscheinungsbild zu prüfen. Wir werden alle Testansichten im Ordner [tests_views] des Projekts sammeln:

Um die Ansicht V [vue-authentification.html] zu testen, müssen wir das Datenmodell M erstellen, das darin angezeigt wird. Dies tun wir mit dem Skript [test_vue_authentification.py]:
Kommentare
- Zeilen 1–3: Wir erstellen eine Flask-Anwendung, deren einziger Zweck darin besteht, die Ansicht [authentication-view.html] anzuzeigen (Zeile 22);
- Zeile 7: Die Anwendung hat nur eine einzige Service-URL;
- Zeilen 9–20: Die Authentifizierungsansicht enthält dynamische Teile, die vom [model]-Objekt gesteuert werden. Dieses Objekt wird als View-Modell bezeichnet. Gemäß einer der beiden Definitionen für das Akronym MVC haben wir hier das M in MVC ( ). Bei der Definition der Ansicht [authentication-view.html] haben wir drei dynamische Werte identifiziert:
- [model.error]: ein Boolescher Wert, der angibt, ob eine Fehlermeldung angezeigt werden soll;
- [model.errors]: eine HTML-Liste mit Fehlermeldungen;
- [model.login]: die Anmeldedaten eines Benutzers;
Wir müssen daher diese drei dynamischen Werte definieren.
- Zeilen 9–20: Wir definieren die drei dynamischen Elemente der Authentifizierungsansicht;
Um den Test auszuführen, starten wir das Skript [tests_views/test_vue_authentification.py] und rufen die URL [/localhost:5000/] auf:
Wir setzen diese visuellen Tests fort, bis wir mit dem Ergebnis zufrieden sind.

32.5.5. Berechnung des Ansichtsmodells
Sobald das visuelle Erscheinungsbild der Ansicht festgelegt wurde, können wir mit der Berechnung des Ansichtsmodells unter realen Bedingungen fortfahren. Ansichtsmodelle werden von Klassen generiert, die sich im Ordner [models_for_views] befinden:

Jede Klasse, die ein Ansichtsmodell generiert, implementiert die folgende Schnittstelle [InterfaceModelForView]:
from abc import ABC, abstractmethod
from flask import Request
from werkzeug.local import LocalProxy
class InterfaceModelForView(ABC):
@abstractmethod
def get_model_for_view(self, request: Request, session: LocalProxy, config: dict, résultat: dict) -> dict:
pass
- Zeilen 8–10: Die Methode [get_model_for_view] ist dafür zuständig, ein in einem Wörterbuch gekapseltes View-Modell zu erzeugen. Dazu erhält sie die folgenden Informationen:
- [request, session, config] sind dieselben Parameter, die auch vom Aktionscontroller verwendet werden. Sie werden daher ebenfalls an das Modell übergeben;
- Der Controller hat ein Ergebnis [result] erzeugt, das ebenfalls an das Modell übergeben wird. Dieses Ergebnis enthält ein wichtiges Element [status], das angibt, wie die Ausführung der aktuellen Aktion verlaufen ist. Das Modell wird diese Informationen verwenden;
Wir haben gesehen, dass in der Konfiguration der Anwendung [config] die von den Controllern zurückgegebenen Statuscodes verwendet werden, um die anzuzeigende HTML-Ansicht festzulegen:
# les vues HTML et leurs modèles dépendent de l'état rendu par le contrôleur
"views": [
{
# vue d'authentification
"états": [
# /init-session réussite
700,
# /authentifier-utilisateur échec
201
],
"view_name": "views/vue-authentification.html",
"model_for_view": ModelForAuthentificationView()
},
{
# vue du calcul de l'impôt
"états": [
# /authentifier-utilisateur réussite
200,
# /calculer-impot réussite
300,
# /calculer-impot échec
301,
# /afficher-calcul-impot
800
],
"view_name": "views/vue-calcul-impot.html",
"model_for_view": ModelForCalculImpotView()
},
{
# vue de la liste des simulations
"états": [
# /lister-simulations
500,
# /supprimer-simulation
600
],
"view_name": "views/vue-liste-simulations.html",
"model_for_view": ModelForListeSimulationsView()
}
],
# vue des erreurs inattendues
"view-erreurs": {
"view_name": "views/vue-erreurs.html",
"model_for_view": ModelForErreursView()
},
# redirections
"redirections": [
{
"états": [
400, # /fin-session réussite
],
# redirection vers
"to": "/init-session/html",
}
],
}
Es sind also die Statuscodes [700, 201] (Zeilen 7 und 9), die dazu führen, dass die Authentifizierungsansicht angezeigt wird. Um die Bedeutung dieser Codes zu verstehen, können wir uns die [Postman]-Tests ansehen, die an der JSON-Anwendung durchgeführt wurden:
- [init-session-json-700]: 700 ist der Statuscode nach einer erfolgreichen [init-session]-Aktion: Das leere Authentifizierungsformular wird dann angezeigt;
- [authenticate-user-201]: 201 ist der Statuscode nach einer fehlgeschlagenen [authenticate-user]-Aktion (unbekannte Anmeldedaten): Das Authentifizierungsformular wird dann angezeigt, damit die Anmeldedaten korrigiert werden können;
Da wir nun wissen, wann das Authentifizierungsformular angezeigt werden soll, können wir sein Modell in [ModelForAuthentificationView] (Zeile 12) definieren:
Kommentare
- Zeile 8: Die Methode [get_model_for_view] der Authentifizierungsansicht muss ein Wörterbuch mit drei Schlüsseln [error, errors, login] zurückgeben. Diese Berechnung basiert auf dem vom Aktionscontroller zurückgegebenen Statuscode;
- Zeile 12: Wir rufen den Statuscode ab, der von dem Controller zurückgegeben wurde, der die aktuelle Aktion bearbeitet hat;
- Zeilen 14–29: Das Modell hängt von diesem Statuscode ab;
- Zeilen 15–18: Fall, in dem ein leeres Authentifizierungsformular angezeigt werden muss;
- Zeilen 20–29: Fall einer fehlgeschlagenen Authentifizierung: Wir zeigen den vom Benutzer eingegebenen Benutzernamen an und geben eine Fehlermeldung aus. Der Benutzer kann dann einen weiteren Authentifizierungsversuch unternehmen;
- Zeile 22: Der ursprünglich vom Benutzer eingegebene Benutzername kann aus der Client-Anfrage abgerufen werden;
- Zeile 24: Es wird angezeigt, dass Fehler anzuzeigen sind;
- Zeilen 26–29: Im Falle eines Fehlers enthält result[‘response’] eine Liste der Fehler;
32.5.6. Erstellen von HTML-Antworten
Kehren wir zum MVC-Modell der HTML-Anwendung zurück:
- in 2 (2a, 2b): Der Controller führt eine Aktion aus;
- in 3 (3a, 3b, 3c): Eine Ansicht wird ausgewählt und an den Client gesendet;
In [3a] wird ein Antworttyp (JSON, XML, HTML) ausgewählt. Wir haben gesehen, wie JSON- und XML-Antworten generiert werden, aber noch nicht HTML-Antworten. Diese werden von der Klasse [HtmlResponse] generiert:

Erinnern wir uns daran, wie der an den Benutzer zu sendende Antworttyp im Hauptskript [main] bestimmt wird:
….
# on construit la réponse à envoyer
response_builder = config["responses"][type_response]
response, status_code = response_builder \
.build_http_response(request, session, config, status_code, résultat)
# on envoie la réponse
return response, status_code
wobei in Zeile 3 config['responses'] das folgende Wörterbuch ist:
# les différents types de réponse (json, xml, html)
"responses": {
"json": JsonResponse(),
"html": HtmlResponse(),
"xml": XmlResponse()
},
Es ist also die Klasse [HtmlResponse], die die HTML-Antwort generiert. Ihr Code lautet wie folgt:
- Zeile 11: Die Methode [build_http_response], die für die Generierung der HTML-Antwort zuständig ist, erhält die folgenden Parameter:
- [request, session, dict]: Dies sind die Parameter, die der Controller zur Verarbeitung der aktuellen Aktion verwendet;
- [status_code, result] sind die beiden Ergebnisse, die von diesem Controller erzeugt werden;
- Zeile 14: Wie bereits erwähnt, hängt die HTML-Antwort des Servers vom Statuscode ab, der im Wörterbuch [result] enthalten ist;
- Zeilen 16–22: Weiterleitungen werden zuerst behandelt. Wir werden dies vorerst ignorieren, bis wir auf ein Beispiel für eine Weiterleitung stoßen. Beachten Sie, dass Weiterleitungen typischerweise ein Anwendungsfall für den HTML-Server sind. Dies tritt bei JSON- oder XML-Servern nicht auf;
- Zeilen 24–41: Wir suchen unter den Views nach derjenigen, deren [states]-Liste den gewünschten Zustand enthält;
- Zeilen 42–46: Wenn keine Ansicht gefunden wird, handelt es sich um einen unerwarteten Fehler. Schauen wir uns ein Beispiel an. Im normalen Betrieb der Anwendung sollte die Aktion [/delete-simulation] niemals fehlschlagen. Tatsächlich werden wir sehen, dass diese Löschung von Simulationen über vom Code generierte Links erfolgt. Diese Links sind gültig und können keinen Fehler verursachen. Wie wir jedoch gesehen haben, kann der Benutzer die URL [/delete-simulation/id] direkt eingeben und so einen Fehler auslösen. In diesem Fall gibt der [SupprimerSimulationController] einen Statuscode von 601 zurück. Dieser Statuscode ist jedoch nicht in der Liste der Statuscodes enthalten, die die Anzeige einer HTML-Seite auslösen. Daher wird die Fehleransicht angezeigt. Sie ist in der Konfiguration wie folgt definiert:
# vue des erreurs inattendues
"view-erreurs": {
"view_name": "views/vue-erreurs.html",
"model_for_view": ModelForErreursView()
},
- Zeile 49: Sobald wir wissen, welche Ansicht angezeigt werden soll, rufen wir die Klasse ab, die deren Vorlage generiert. Diese Klasse befindet sich ebenfalls in der [config]-Konfiguration;
- Zeile 50: Sobald diese Klasse gefunden wurde, generieren wir das Modell der Ansicht;
- Zeile 52: Sobald das Modell M der Ansicht V berechnet wurde, können wir den HTML-Code der Ansicht generieren;
- Zeilen 54–55: Wir erstellen die HTTP-Antwort mit einem HTML-Body;
- Zeilen 56–57: Wir geben die HTTP-Antwort mit ihrem Statuscode zurück;
32.5.7. Tests [Postman]
Wir führen Anfragen aus, die die Codes [700, 201] zurückgeben, wodurch die Authentifizierungsansicht angezeigt wird:
- [init-session-html-700]: 700 ist der Statuscode nach einer erfolgreichen [init-session]-Aktion; anschließend wird das leere Authentifizierungsformular angezeigt;
- [authenticate-user-201]: 201 ist der Statuscode nach einer fehlgeschlagenen [authenticate-user]-Aktion (unbekannte Anmeldedaten): Das Authentifizierungsformular wird dann angezeigt, damit die Daten korrigiert werden können;
Verwenden Sie diese einfach wieder und prüfen Sie, ob sie die Authentifizierungsansicht korrekt anzeigen. Hier sind zwei Beispiele:
Fall 1: [init-session-html-700], Start einer HTML-Sitzung;

Die Antwort lautet wie folgt:

- In [5] können Sie im [Vorschau]-Modus die empfangene HTML-Seite anzeigen;
- in [6] sehen wir das erwartete leere Formular;
- in [7] ist Postman dem Link zum Seitenbild nicht gefolgt;
- in [8] bietet der [Raw]-Modus Zugriff auf den empfangenen HTML-Code;

- In [3] der Link, den Postman nicht laden konnte. Es wurde der Wert des Attributs [alt=alternative] angezeigt, der erscheint, wenn das Bild nicht geladen werden kann. Hier ist es eher so, dass Postman es nicht laden wollte. Sie können dies überprüfen, indem Sie die URL [http://localhost:5000/static/images.logo.jpg] mit Postman aufrufen:
Fall 2: [user-authentication-201], Authentifizierungsfehler

Führen wir nun nach erfolgreicher Initialisierung einer HTML-Sitzung eine fehlerhafte Authentifizierung durch:

Oben:
- bei [4,7]: Die Anfrage sendet die Zeichenfolge [user=bernard&password=thibault];
Die Antwort lautet wie folgt:

- bei [4] wird eine Fehlermeldung angezeigt;
- bei [3] wurde erneut der falsche Benutzer angezeigt;
32.5.8. Fazit
Wir konnten die Ansicht [vue-authentification.html] testen, ohne die anderen Ansichten geschrieben zu haben. Dies war möglich, weil:
- alle Controller geschrieben sind;
- [Postman] es uns ermöglicht, Anfragen an den Server zu senden, ohne dass alle Ansichten erforderlich sind. Beim Schreiben von Controllern müssen Sie darauf vorbereitet sein, Anfragen zu bearbeiten, die keine Ansicht zulassen würde. Sie sollten niemals von vornherein davon ausgehen, dass „diese Anfrage unmöglich ist“. Sie müssen dies überprüfen;
32.6. Die Ansicht zur Steuerberechnung

32.6.1. Übersicht über die Ansicht
Die Ansicht zur Steuerberechnung sieht wie folgt aus:

Die Ansicht besteht aus drei Teilen:
- 1: Das obere Banner wird durch das bereits vorgestellte Fragment [v-bandeau.html] generiert;
- 2: das Formular zur Steuerberechnung, das vom Fragment [v-calcul-impot.html] generiert wird;
- 3: ein Menü mit zwei Links, das vom Fragment [v-menu.html] generiert wird;
Die Ansicht zur Steuerberechnung wird durch den folgenden Code [vue-calcul-impot.html] generiert:
<!-- document HTML -->
<!doctype html>
<html lang="fr">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
<title>Application impôts</title>
</head>
<body>
<div class="container">
<!-- headband -->
{% include "fragments/v-bandeau.html" %}
<!-- two-column line -->
<div class="row">
<!-- the menu -->
<div class="col-md-3">
{% include "fragments/v-menu.html" %}
</div>
<!-- calculation form -->
<div class="col-md-9">
{% include "fragments/v-calcul-impot.html" %}
</div>
</div>
<!-- success stories -->
{% if modèle.success %}
<!-- a success alert is displayed -->
<div class="row">
<div class="col-md-3">
</div>
<div class="col-md-9">
<div class="alert alert-success" role="alert">
{{modèle.impôt}}</br>
{{modèle.décôte}}</br>
{{modèle.réduction}}</br>
{{modèle.surcôte}}</br>
{{modèle.taux}}</br>
</div>
</div>
</div>
{% endif %}
{% if modèle.error %}
<!-- 9-column error list -->
<div class="row">
<div class="col-md-3">
</div>
<div class="col-md-9">
<div class="alert alert-danger" role="alert">
Les erreurs suivantes se sont produites :
<ul>{{modèle.erreurs | safe}}</ul>
</div>
</div>
</div>
{% endif %}
</div>
</body>
</html>
Kommentare
- Wir kommentieren nur neue Funktionen aus, die noch nicht aufgetreten sind;
- Zeile 16: Einbindung des oberen Banners der Ansicht in die erste Bootstrap-Zeile der Ansicht;
- Zeile 21: Einbindung des Menüs, das drei Spalten der zweiten Bootstrap-Zeile der Ansicht einnehmen wird (Zeilen 18, 20);
- Zeile 25: Einbindung des Formulars zur Steuerberechnung, das neun Spalten (Zeile 24) der zweiten Bootstrap-Zeile der Ansicht einnimmt (Zeile 18);
- Zeilen 30–46: Wenn die Steuerberechnung erfolgreich ist [model.success=True], wird das Ergebnis der Steuerberechnung in einem grünen Rahmen angezeigt (Zeilen 37–43). Dieses Feld befindet sich in der dritten Bootstrap-Zeile der Ansicht (Zeile 32) und nimmt neun Spalten (Zeile 36) rechts von drei leeren Spalten (Zeilen 33–35) ein. Dieses Feld befindet sich somit unterhalb des Steuerberechnungsformulars;
- Zeilen 48–61: Wenn die Steuerberechnung fehlschlägt [model.error=True], wird eine Fehlermeldung in einem rosa Container angezeigt (Zeilen 55–58). Dieser Rahmen befindet sich in der dritten Bootstrap-Zeile der Ansicht (Zeile 50) und nimmt neun Spalten (Zeile 54) rechts von drei leeren Spalten (Zeilen 51–53) ein. Dieser Rahmen befindet sich daher ebenfalls unterhalb des Formulars zur Steuerberechnung;
32.6.2. Das Fragment [v-calcul-impot.html]
Das Fragment [v-calcul-impot.html] zeigt das Formular zur Steuerberechnung der Webanwendung an:
Der Code für das Fragment [v-calcul-impot.html] lautet wie folgt:

<!-- form HTML posted -->
<form method="post" action="/calculer-impot">
<!-- 12-column message on blue background -->
<div class="col-md-12">
<div class="alert alert-primary" role="alert">
<h4>Remplissez le formulaire ci-dessous puis validez-le</h4>
</div>
</div>
<!-- form elements -->
<fieldset class="form-group">
<!-- first row of 9 columns -->
<div class="row">
<!-- 4-column wording -->
<legend class="col-form-label col-md-4 pt-0">Etes-vous marié(e) ou pacsé(e)?</legend>
<!-- 5-column radio buttons-->
<div class="col-md-5">
<div class="form-check">
<input class="form-check-input" type="radio" name="marié" id="gridRadios1" value="oui" {{modèle.checkedOui}}>
<label class="form-check-label" for="gridRadios1">
Oui
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="marié" id="gridRadios2" value="non" {{modèle.checkedNon}}>
<label class="form-check-label" for="gridRadios2">
Non
</label>
</div>
</div>
</div>
<!-- second row of 9 columns -->
<div class="form-group row">
<!-- 4-column wording -->
<label for="enfants" class="col-md-4 col-form-label">Nombre d'enfants à charge</label>
<!-- 5-column numerical entry field for number of children -->
<div class="col-md-5">
<input type="number" min="0" step="1" class="form-control" id="enfants" name="enfants" placeholder="Nombre d'enfants à charge" value="{{modèle.enfants}}" required>
</div>
</div>
<!-- third row of 9 columns -->
<div class="form-group row">
<!-- 4-column wording -->
<label for="salaire" class="col-md-4 col-form-label">Salaire annuel net imposable</label>
<!-- 5-column numeric input field for wages -->
<div class="col-md-5">
<input type="number" min="0" step="1" class="form-control" id="salaire" name="salaire" placeholder="Salaire annuel net imposable" aria-describedby="salaireHelp" value="{{modèle.salaire}}" required>
<small id="salaireHelp" class="form-text text-muted">Arrondissez à l'euro inférieur</small>
</div>
</div>
<!-- fourth row, [submit] button on 5 columns -->
<div class="form-group row">
<div class="col-md-5">
<button type="submit" class="btn btn-primary">Valider</button>
</div>
</div>
</fieldset>
</form>
Kommentare
- Zeile 2: Das HTML-Formular wird (Attribut „method“) an die URL [/calculer-impot] (Attribut „action“) übermittelt. Die übermittelten Werte entsprechen den Werten der Eingabefelder:
- der Wert des ausgewählten Optionsfelds im Formular:
- [married=yes], wenn das Optionsfeld [Yes] ausgewählt ist (Zeilen 17–22). [married] ist der Wert des Attributs [name] in Zeile 18, [yes] ist der Wert des Attributs [value] in Zeile 18;
- [married=no], wenn das Optionsfeld [Nein] ausgewählt ist (Zeilen 23–28). [married] ist der Wert des Attributs [name] in Zeile 24, und [no] ist der Wert des Attributs [value] in Zeile 24;
- der Wert des numerischen Eingabefelds in Zeile 37 in der Form [children=xx], wobei [children] der Wert des Attributs [name] in Zeile 37 ist und [xx] der vom Benutzer über die Tastatur eingegebene Wert;
- der Wert des numerischen Eingabefelds in Zeile 46 in der Form [salary=xx], wobei [salary] der Wert des Attributs [name] in Zeile 46 ist und [xx] der vom Benutzer über die Tastatur eingegebene Wert;
Schließlich hat der übermittelte Wert die Form [married=xx&children=yy&salary=zz].
- (Fortsetzung)
- Die eingegebenen Werte werden übermittelt, wenn der Benutzer in Zeile 53 auf die Schaltfläche [submit] klickt;
- Zeilen 16–30: Die beiden Optionsfelder:
![]()
Die beiden Optionsfelder gehören zur selben Optionsfeldgruppe, da sie dasselbe [name]-Attribut haben (Zeilen 18, 24). Der Browser stellt sicher, dass innerhalb einer Optionsfeldgruppe zu jedem Zeitpunkt nur eines ausgewählt ist. Daher wird durch das Klicken auf eines das zuvor ausgewählte deaktiviert;
- Es handelt sich aufgrund des Attributs [type="radio"] um Optionsfelder (Zeilen 18, 24);
- Wenn das Formular angezeigt wird (vor der Dateneingabe), muss eines der Optionsfelder ausgewählt sein: Fügen Sie dazu einfach das Attribut [checked='checked'] zum entsprechenden <input type="radio">-Tag hinzu. Dies wird mithilfe dynamischer Variablen erreicht:
- [model.checkedYes] in Zeile 18;
- [model->checkedNo] in Zeile 24;
Diese Variablen werden Teil der Ansichtsvorlage sein.
- Zeile 37: ein numerisches Eingabefeld [type="number"] mit einem Mindestwert von 0 [min="0"]. In modernen Browsern bedeutet dies, dass der Benutzer nur eine Zahl >=0 eingeben kann. In denselben modernen Browsern kann die Eingabe über einen Schieberegler erfolgen, der nach oben oder unten geklickt werden kann. Das Attribut [step="1"] in Zeile 37 gibt an, dass der Schieberegler in Schritten von 1 arbeitet. Folglich akzeptiert der Schieberegler nur ganzzahlige Werte im Bereich von 0 bis n in Schritten von 1. Bei der manuellen Eingabe bedeutet dies, dass Zahlen mit Dezimalstellen nicht akzeptiert werden;
- Zeile 37: In bestimmten Ansichten muss das untergeordnete Eingabefeld mit dem zuletzt in diesem Feld vorgenommenen Eintrag vorbelegt sein. Dazu verwenden wir das Attribut [value], das den im Eingabefeld anzuzeigenden Wert festlegt. Dieser Wert ist dynamisch und wird durch die Variable [model.children] generiert;
- Zeile 37: Das Attribut [required] zwingt den Benutzer, Daten einzugeben, damit das Formular validiert werden kann;
- Zeile 46: Für das Feld „Gehalt“ gelten dieselben Erläuterungen wie für das Feld „Kinder“;
- Zeile 53: Die Schaltfläche [submit] löst eine POST-Anfrage der eingegebenen Werte an die URL [/calculer-impot] (Zeile 2) aus;
![]()
32.6.3. Das Fragment [v-menu.html]
Dieses Fragment zeigt links neben dem Formular zur Steuerberechnung ein Menü an:

Der Code für dieses Fragment lautet wie folgt:
<!-- bootstrap menu -->
<nav class="nav flex-column">
<!-- display a list of links HTML -->
{% for optionMenu in modèle.optionsMenu %}
<a class="nav-link" href="{{optionMenu.url}}">{{optionMenu.text}}</a>
{% endfor %}
</nav>
Kommentare
- Zeilen 2–7: Der HTML-Tag [nav] umschließt einen Abschnitt des HTML-Dokuments, der Navigationslinks zu anderen Dokumenten enthält;
- Zeile 5: Der HTML-Tag [a] leitet einen Navigationslink ein:
- [optionMenu.url]: ist die URL, zu der der Benutzer weitergeleitet wird, wenn er auf den Link [optionMenu.text] klickt. Der Browser führt dann einen [GET optionMenu.url]-Vorgang aus. [optionMenu.url] ist eine absolute URL relativ zum Stammverzeichnis der Anwendung [http://machine:port/path]. Daher erstellen wir in [1] den folgenden Link:
- Zeile 5: Die Vorlage [modèle.optionsMenu] des Fragments ist eine Liste im folgenden Format:
- Zeilen 2, 7: Die CSS-Klassen [nav, flex-column, nav-link] sind Bootstrap-Klassen, die das Erscheinungsbild des Menüs definieren;
32.6.4. Visueller Test
Wir sammeln diese verschiedenen Elemente im Ordner [Tests] und erstellen eine Testvorlage für die Ansicht [vue-calcul-impot.html]:

Das Testskript [test_vue_calcul_impot] sieht wie folgt aus:
Kommentare
- Zeilen 9–34: Initialisierung aller dynamischen Teile der Ansicht [vue-calcul-impot.html] sowie der Fragmente [v-calcul-impot.html] und [v-menu.html];
- Zeile 36: Die Ansicht [vue-calcul-impot.html] wird angezeigt;
Wenn wir das Testskript [test_vue_calcul_impot] ausführen, erhalten wir folgendes Ergebnis:
Wir arbeiten an dieser Ansicht, bis wir mit dem visuellen Ergebnis zufrieden sind. Anschließend können wir die Ansicht in die derzeit in Entwicklung befindliche Webanwendung integrieren.

32.6.5. Berechnung des View-Modells
Sobald das visuelle Erscheinungsbild der Ansicht festgelegt ist, können wir mit der Berechnung des Ansichtsmodells unter realen Bedingungen fortfahren. Sehen wir uns die Zustandscodes an, die zu dieser Ansicht führen. Sie sind in der Konfigurationsdatei zu finden:
{
# vue du calcul de l'impôt
"états": [
# /authentifier-utilisateur réussite
200,
# /calculer-impot réussite
300,
# /calculer-impot échec
301,
# /afficher-calcul-impot
800
],
"view_name": "views/vue-calcul-impot.html",
"model_for_view": ModelForCalculImpotView()
},
Es sind also die Statuscodes [200, 300, 301, 800], die die Anzeige der Steuerberechnungsansicht auslösen. Um die Bedeutung dieser Codes zu verstehen, können wir uns die [Postman]-Tests ansehen, die an der JSON-Anwendung durchgeführt wurden:
- [authenticate-user-200]: 200 ist der Statuscode nach einer erfolgreichen [authenticate-user]-Aktion; anschließend wird das leere Steuerberechnungsformular angezeigt;
- [calculate-tax-300]: 300 ist der Statuscode, der nach einer erfolgreichen [calculate-tax]-Operation zurückgegeben wird. Anschließend wird das Berechnungsformular angezeigt, in dem die eingegebenen Daten und der Steuerbetrag zu sehen sind. Der Benutzer kann dann eine weitere Berechnung durchführen;
- Der Statuscode [301] wird bei einer fehlerhaften Steuerberechnung zurückgegeben;
- Der Statuscode [800] wird später erläutert. Wir sind ihm bisher noch nicht begegnet;
Da wir nun wissen, wann das Steuerberechnungsformular angezeigt werden soll, können wir sein Modell in der Klasse [ModelForCalculImpotView] definieren:

Kommentare
- Zeile 12: Die anzuzeigende Ansicht hängt vom vom Controller zurückgegebenen Statuscode ab;
- Zeilen 14–21: Anzeige eines leeren Formulars;
- Zeilen 22–35: erfolgreiche Steuerberechnung. Die eingegebenen Werte und der Steuerbetrag werden erneut angezeigt;
- Zeilen 36–47: Fall, in dem die Steuerberechnung fehlschlägt;
- Zeilen 49–52: Berechnung der beiden Menüoptionen;
32.6.6. Tests [Postman]
Wir initialisieren eine HTML-Sitzung mit der Anfrage [init-session-html-700] und authentifizieren uns anschließend mit der Anfrage [authenticate-user-200]. Als Nächstes verwenden wir die folgende Anfrage [calculate-tax-300]:
Die Serverantwort lautet wie folgt:


Probieren wir nun die folgende Anfrage [calculate-tax-301] aus:

Die Serverantwort lautet wie folgt:
Versuchen wir nun ein unerwartetes Szenario: eines, bei dem Parameter in der POST-Anfrage fehlen. Dieses Szenario ist im normalen Anwendungsbetrieb nicht möglich. Aber jeder kann an einer HTTP-Anfrage „herumbasteln“, so wie wir es gerade tun:


- In [6] haben wir das Häkchen beim gesendeten Parameter [married] entfernt;
Die Antwort des Servers lautet wie folgt:

- in [3], die Fehlermeldung des Servers;
In dieser Anwendung hatten wir die Wahl. Wir hätten diesem Fehlerfall einen Statuscode zuweisen können, der auf die Seite für unerwartete Fehler weiterleitet. In dieser Anwendung haben wir für jeden Controller zwei Statuscodes ausgewählt:
- [xx0]: für Erfolg;
- [xx1]: bei Fehlschlag;
Für Fehlerfälle können wir verschiedene Statuscodes verwenden, um eine detailliertere Fehlerbehandlung zu ermöglichen. Wir hätten beispielsweise folgende verwenden können:
- [xx1]: für Fehler, die auf der Seite angezeigt werden sollen, die den Fehler verursacht hat;
- [xx2]: für unerwartete Fehler während der normalen Nutzung der Anwendung;
32.7. Die Simulationslistenansicht

32.7.1. Übersicht über die Ansicht
Die Ansicht, in der die Liste der Simulationen angezeigt wird, sieht wie folgt aus:

Die durch den Code [vue-liste-simulations.html] generierte Ansicht besteht aus drei Teilen:
- 1: Das obere Banner wird durch das bereits vorgestellte Fragment [v-banner.html] generiert;
- 3: die durch das Fragment [v-simulation-list.html] generierte Simulationstabelle;
- 2: ein Menü mit zwei Links, das durch das bereits vorgestellte Fragment [v-menu.html] generiert wird;
Die Simulationsansicht wird durch den folgenden Code [vue-liste-simulations.html] generiert:
<!-- document HTML -->
<!doctype html>
<html lang="fr">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
<title>Application impôts</title>
</head>
<body>
<div class="container">
<!-- headband -->
{% include "fragments/v-bandeau.html" %}
<!-- two-column line -->
<div class="row">
<!-- three-column menu-->
<div class="col-md-3">
{% include "fragments/v-menu.html" %}
</div>
<!-- list of simulations on 9 columns-->
<div class="col-md-9">
{% include "fragments/v-liste-simulations.html" %}
</div>
</div>
</div>
</body>
</html>
Kommentare
- Zeile 16: Einfügen des Anwendungsbanners [1];
- Zeile 21: Einfügen des Menüs [2]. Es wird in drei Spalten unterhalb des Banners angezeigt;
- Zeile 26: Einfügen der Simulationstabelle [3]. Sie wird in neun Spalten unterhalb des Banners und rechts neben dem Menü angezeigt;
Wir haben bereits zwei der drei Fragmente dieser Ansicht kommentiert:
Das Fragment [v-liste-simulations.html] lautet wie folgt:
{% if modèle.simulations is undefined or modèle.simulations|length==0 %}
<!-- message on blue background -->
<div class="alert alert-primary" role="alert">
<h4>Votre liste de simulations est vide</h4>
</div>
{% endif %}
{% if modèle.simulations is defined and modèle.simulations|length!=0 %}
<!-- message on blue background -->
<div class="alert alert-primary" role="alert">
<h4>Liste de vos simulations</h4>
</div>
<!-- simulation table -->
<table class="table table-sm table-hover table-striped">
<!-- headers of the six table columns -->
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Marié</th>
<th scope="col">Nombre d'enfants</th>
<th scope="col">Salaire annuel</th>
<th scope="col">Montant impôt</th>
<th scope="col">Surcôte</th>
<th scope="col">Décôte</th>
<th scope="col">Réduction</th>
<th scope="col">Taux</th>
<th scope="col"></th>
</tr>
</thead>
<!-- table body (data displayed) -->
<tbody>
<!-- display each simulation by browsing the simulation table -->
{% for simulation in modèle.simulations %}
<!-- display a table row with 6 columns - <tr> tag -->
<!-- column 1: row header (simulation no.) - <th scope='row' tag -->
<!-- column 2: parameter value [married] - <td> tag -->
<!-- column 3: parameter value [children] - <td> tag -->
<!-- column 4: parameter value [salary] - <td> tag -->
<!-- column 5: [tax] parameter value - <td> tag -->
<!-- column 6: parameter value [surcôte] - <td> tag -->
<!-- column 7: parameter value [discount] - <td> tag -->
<!-- column 8: parameter value [reduction] - <td> tag -->
<!-- column 9: parameter value [rate] (of tax) - <td> tag -->
<!-- column 10: link to delete simulation - <td> tag -->
<tr>
<th scope="row">{{simulation.id}}</th>
<td>{{simulation.marié}}</td>
<td>{{simulation.enfants}}</td>
<td>{{simulation.salaire}}</td>
<td>{{simulation.impôt}}</td>
<td>{{simulation.surcôte}}</td>
<td>{{simulation.décôte}}</td>
<td>{{simulation.réduction}}</td>
<td>{{simulation.taux}}</td>
<td><a href="/supprimer-simulation/{{simulation.id}}">Supprimer</a></td>
</tr>
{% endfor %}
</tr>
</tbody>
</table>
{% endif %}
Kommentare
- Eine HTML-Tabelle wird mit dem Tag <table> erstellt (Zeilen 15 und 62);
- Die Spaltenüberschriften der Tabelle werden innerhalb eines <thead>-Tags (Tabellenkopf, Zeilen 17, 30) definiert. Das <tr>-Tag (Tabellenzeile, Zeilen 18 und 29) definiert eine Zeile. Zeilen 19–28: Das <th>-Tag (Tabellenkopf) definiert eine Spaltenüberschrift. Es gibt zehn davon. [scope="col"] gibt an, dass die Überschrift für die Spalte gilt. [scope="row"] gibt an, dass die Überschrift für die Zeile gilt;
- Zeilen 32–61: Das <tbody>-Tag umschließt die von der Tabelle angezeigten Daten;
- Zeilen 47–58: Das <tr>-Tag umrahmt eine Zeile der Tabelle;
- Zeile 48: Das <th scope=’row’>-Tag definiert die Zeilenüberschrift. Der Browser hebt diese Überschrift hervor;
- Zeilen 49–57: Jedes td-Tag (Tabellendaten) definiert eine Spalte der Zeile;
- Zeile 34: Die Liste der Simulationen befindet sich im Modell [model.simulations], bei dem es sich um eine Liste von Wörterbüchern handelt;
- Zeile 57: Ein Link zum Löschen der Simulation. Die URL verwendet die Nummer der in der Zeile angezeigten Simulation;
32.7.2. Visueller Test
Wir erstellen ein Testskript für die Ansicht [view-simulation-list.html]:
Das Skript [test_simulation_list_view] lautet wie folgt:

Kommentare
- Zeilen 12–35: Dem Modell werden zwei Simulationen hinzugefügt
- Zeilen 37–39: die Tabelle mit den Menüoptionen;
Lassen Sie uns diese Ansicht anzeigen, indem wir dieses Skript ausführen. Wir erhalten das folgende Ergebnis:

Wir arbeiten so lange an dieser Ansicht, bis wir mit ihrem Aussehen zufrieden sind. Anschließend können wir damit fortfahren, die Ansicht in die Webanwendung zu integrieren, die wir gerade entwickeln.
32.7.3. Berechnung des Ansichtsmodells
Sobald das visuelle Erscheinungsbild der Ansicht festgelegt ist, können wir mit der Berechnung des Ansichtsmodells unter realen Bedingungen fortfahren. Sehen wir uns die Statuscodes an, die zu dieser Ansicht führen. Sie sind in der Konfigurationsdatei zu finden:

{
# vue de la liste des simulations
"états": [
# /lister-simulations
500,
# /supprimer-simulation
600
],
"view_name": "views/vue-liste-simulations.html",
"model_for_view": ModelForListeSimulationsView()
}
Es sind also die Statuscodes [500, 600], die die Anzeige der Simulationsansicht auslösen. Um die Bedeutung dieser Codes zu verstehen, können wir uns die [Postman]-Tests ansehen, die an der JSON-Anwendung durchgeführt wurden:
- [list-simulations-500]: 500 ist der Statuscode nach einer erfolgreichen [list-simulations]-Aktion: Die Liste der vom Benutzer durchgeführten Simulationen wird daraufhin angezeigt;
- [delete-simulation-600]: 600 ist der Statuscode nach einer erfolgreichen [delete-simulation]-Aktion. Die nach dieser Löschung erhaltene neue Liste der Simulationen wird dann angezeigt;
Da wir nun wissen, wann die Liste der Simulationen angezeigt werden soll, können wir ihr Modell in der Klasse [ModelForListeSimulationsView] definieren:
Kommentare
- Zeile 13: Die anzuzeigenden Simulationen befinden sich in [result["response"]];
- Zeilen 15–17: die anzuzeigenden Menüoptionen;
32.7.4. [Postman] Tests
Wir
- initialisieren eine HTML-Sitzung;
- authentifizieren;
- führen drei Steuerberechnungen durch;
Der Test [lister-simulations-500] ermöglicht es uns, einen 500-Statuscode zu erhalten. Er entspricht einer Anfrage zur Anzeige der Simulationen:

Die Serverantwort lautet wie folgt:

Der Test [delete-simulation-600] gibt den Statuscode 600 zurück. Hier löschen wir Simulation Nr. 2.
Das zurückgegebene Ergebnis ist eine Liste von Simulationen, in der eine Simulation fehlt:


32.8. Anzeigen unerwarteter Fehler
Hier bezeichnen wir einen unerwarteten Fehler als einen Fehler, der bei normaler Nutzung der Webanwendung nicht hätte auftreten dürfen. Zum Beispiel die Anforderung einer Steuerberechnung ohne Authentifizierung. Nichts hindert einen Benutzer daran, die URL [/tax-calculation] direkt in seinen Browser einzugeben. Außerdem kann er, wie wir gesehen haben, eine POST-Anfrage an die URL [/tax-calculation] senden, ohne die erwarteten Parameter anzugeben. Wir haben gesehen, dass unsere Webanwendung wusste, wie sie auf diese Anfrage korrekt reagieren musste. Wir bezeichnen einen „unerwarteten Fehler“ als einen Fehler, der innerhalb der HTML-Anwendung nicht auftreten sollte. Tritt er dennoch auf, ist es wahrscheinlich, dass jemand versucht, die Anwendung zu „hacken“. Zu Lehrzwecken haben wir uns entschieden, in diesen Fällen eine Fehlerseite anzuzeigen. In der Praxis könnten wir die zuletzt an den Client gesendete Seite erneut anzeigen. Dazu müssen wir lediglich die zuletzt gesendete HTML-Antwort in der Sitzung speichern. Im Falle eines unerwarteten Fehlers geben wir diese Antwort zurück. Auf diese Weise hat der Benutzer den Eindruck, dass der Server nicht auf seine Fehler reagiert, da sich die angezeigte Seite nicht ändert.
32.8.1. Übersicht

Die Ansicht, die unerwartete Fehler anzeigt, sieht wie folgt aus:

Die durch den Code [vue-erreurs.html] generierte Ansicht besteht aus drei Teilen:
- 1: Das obere Banner wird durch das bereits vorgestellte Fragment [v-banner.html] generiert;
- 2: der/die unerwartete(n) Fehler;
- 3: ein Menü mit drei Links, generiert durch das bereits vorgestellte Fragment [v-menu.html];
Die Ansicht für unerwartete Fehler wird durch das folgende Skript [error-view.html] generiert:
<!-- document HTML -->
<!doctype html>
<html lang="fr">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
<title>Application impôts</title>
</head>
<body>
<div class="container">
<!-- 12-column banner -->
{% include "fragments/v-bandeau.html" %}
<!-- two-section line -->
<div class="row">
<!-- 3-column menu-->
<div class="col-md-3">
{% include "fragments/v-menu.html" %}
</div>
<!-- 9-column error list -->
<div class="col-md-9">
<div class="alert alert-danger" role="alert">
Les erreurs inattendues suivantes se sont produites :
<ul>{{modèle.erreurs|safe}}</ul>
</div>
</div>
</div>
</div>
</body>
</html>
Kommentare
- Zeile 16: Einfügen des Anwendungsbanners [1];
- Zeile 21: Einbindung des Menüs [3]. Es wird in drei Spalten unterhalb des Banners angezeigt;
- Zeilen 24–29: Anzeige des Fehlerbereichs über neun Spalten;
- Zeile 25: Diese Anzeige erfolgt in einem Bootstrap-Container mit rosa Hintergrund;
- Zeile 26: Einleitungstext;
- Zeile 27: Der Tag <ul> umschließt eine Aufzählungsliste. Diese Aufzählungsliste wird von der Vorlage [template.errors] bereitgestellt;
Wir haben bereits die beiden Fragmente dieser Ansicht kommentiert:
- [v-banner.html]: im Link-Abschnitt;
- [v-menu.html]: im Link-Abschnitt;
32.8.2. Visueller Test
Wir erstellen ein Testskript für die Ansicht [vue-erreurs.html]:

Kommentare
- Zeilen 11–15: Erstellen der HTML-Liste mit Fehlern;
- Zeilen 17–20: das Array mit den Menüoptionen;
Führen wir dieses Skript aus. Wir erhalten folgendes Ergebnis:
Wir arbeiten an dieser Ansicht, bis wir mit dem visuellen Ergebnis zufrieden sind. Anschließend können wir damit fortfahren, die Ansicht in die Webanwendung zu integrieren, die wir gerade entwickeln.

32.8.3. Berechnung des Ansichtsmodells

Sobald das visuelle Erscheinungsbild der Ansicht festgelegt ist, können wir mit der Berechnung des Ansichtsmodells unter realen Bedingungen fortfahren. Sehen wir uns die Statuscodes an, die zu dieser Ansicht führen. Sie sind in der Konfigurationsdatei zu finden:
# les vues HTML et leurs modèles dépendent de l'état rendu par le contrôleur
"views": [
{
# vue d'authentification
"états": [
# /init-session réussite
700,
# /fin-session
400,
# /authentifier-utilisateur échec
201
],
"view_name": "views/vue-authentification.html",
"model_for_view": ModelForAuthentificationView()
},
{
# vue du calcul de l'impôt
"états": [
# /authentifier-utilisateur réussite
200,
# /calculer-impot réussite
300,
# /calculer-impot échec
301,
# /afficher-calcul-impot
800
],
"view_name": "views/vue-calcul-impot.html",
"model_for_view": ModelForCalculImpotView()
},
{
# vue de la liste des simulations
"états": [
# /lister-simulations
500,
# /supprimer-simulation
600
],
"view_name": "views/vue-liste-simulations.html",
"model_for_view": ModelForListeSimulationsView()
}
],
# vue des erreurs inattendues
"view-erreurs": {
"view_name": "views/vue-erreurs.html",
"model_for_view": ModelForErreursView()
},
Dies sind die Statuscodes, die in den Zeilen 3–41 nicht zu einer HTML-Ansicht führen und die Anzeige der Ansicht für unerwartete Fehler bewirken.
Das Ansichtsmodell [view-errors.html] wird von der folgenden Klasse [ModelForErrorsView] berechnet:
Kommentare
- Zeilen 11–14: Berechnung des Modells [model.errors], das von der Ansicht [view-errors.html] verwendet wird;
- Zeilen 16–197: Berechnung des Modells [model.optionsMenu], das vom Fragment [v-menu.html] verwendet wird;
32.8.4. Tests [Postman]
Wir führen Folgendes durch:
- die Aktion [/init-session/html];
- dann die Aktion [/init-session/x];
Die HTML-Antwort lautet dann wie folgt:

32.9. Implementierung der Menüaktionen der Anwendung
Hier werden wir die Implementierung der Menüaktionen besprechen. Lassen Sie uns die Bedeutung der Links, auf die wir gestoßen sind, noch einmal betrachten
Anzeigen | Link | Ziel | Rolle |
Steuerberechnung | [Liste der Simulationen] | [/list-simulations] | Liste der Simulationen anfordern |
[Ende der Sitzung] | |||
Liste der Simulationen | [Steuerberechnung] | [/anzeige-steuerberechnung] | Steuerberechnung anzeigen |
[Sitzung beenden] | |||
Unerwartete Fehler | [Steuerberechnung] | [/display-tax-calculation] | Steuerberechnung anzeigen |
[Liste der Simulationen] | |||
[Sitzung beenden] |
Es ist wichtig zu beachten, dass das Klicken auf einen Link eine GET-Anfrage an das Ziel des Links auslöst. Die Aktionen [/lister-simulations, /end-session] wurden mithilfe einer GET-Operation implementiert, wodurch wir sie als Linkziele verwenden können. Wenn die Aktion über eine POST-Anfrage ausgeführt wird, ist die Verwendung eines Links nicht mehr möglich, es sei denn, sie wird mit JavaScript kombiniert.
32.9.1. Die Aktion [/display-tax-calculation]
Aus den oben aufgeführten Aktionen geht hervor, dass die Aktion [/display-tax-calculation] noch nicht implementiert wurde. Hierbei handelt es sich um einen Navigationsvorgang zwischen zwei Ansichten: Der JSON- oder XML-Server hat keinen Grund, dies zu implementieren, da dort das Konzept einer Ansicht nicht existiert. Es ist der HTML-Server, der dieses Konzept einführt.
Wir müssen daher die Aktion [/display-tax-calculation] implementieren. Dies ermöglicht es uns, den Ablauf der Implementierung einer Aktion innerhalb des Servers zu überprüfen.
Zunächst müssen wir einen neuen sekundären Controller hinzufügen. Wir nennen ihn [AfficherCalculImpotController]:

Dieser Controller muss zur Konfigurationsdatei [config] hinzugefügt werden:
- Zeile 2: der neue Controller;
- Zeile 28: die neue Aktion und ihr Controller;
- Zeile 51: Der neue Controller gibt den Statuscode 800 zurück. Beim Wechseln der Ansichten darf kein Fehler auftreten. Die angezeigte Ansicht ist die Ansicht [vue-calcul-import.html], die wir untersucht, erklärt und getestet haben;
Der Controller [AfficherCalculImpotController] sieht wie folgt aus:
Kommentare
- Zeile 6: Wie die anderen sekundären Controller implementiert auch der neue Controller die Schnittstelle [InterfaceController];
- Zeile 13: Änderungen an der Ansicht lassen sich einfach umsetzen: Geben Sie einfach einen Statuscode zurück, der der Zielansicht zugeordnet ist, hier Code 800, wie oben zu sehen;
32.9.2. Die Aktion [/end-session]
Die Aktion [/end-session] ist etwas Besonderes. Sie führt nicht direkt zu einer Ansicht, sondern zu einer Weiterleitung. Zur Erinnerung: Weiterleitungen werden in der [config]-Datei wie folgt konfiguriert:
# redirections
"redirections": [
{
"états": [
400, # /fin-session réussi
],
# redirection vers
"to": "/init-session/html",
}
],
Es gibt nur eine Weiterleitung in der Anwendung:
- Wenn der Controller den Statuscode [400] zurückgibt (Zeile 5), muss der Client an die URL [http://machine:port/chemin/init-session/html] weitergeleitet werden (Zeile 8);
Der Statuscode [400] ist der Code, der nach einer erfolgreichen [/fin-session]-Aktion zurückgegeben wird. Warum muss der Client dann auf die URL [/init-session/html] umgeleitet werden? Weil der Aktionscode [/fin-session] den Sitzungstyp aus der Websitzung entfernt. Wir wissen nicht mehr, dass wir uns in einer HTML-Sitzung befinden. Wir müssen eine Umleitung vornehmen. Dies tun wir mithilfe der Aktion [/init-session/html].
HTML-Weiterleitungen werden von der Klasse [HtmlResponse] verarbeitet:
- Die Zeilen 6–12 behandeln Weiterleitungen;
- Zeile 7: config['redirections'] ist eine Liste von Weiterleitungen. Jede Weiterleitung ist ein Wörterbuch mit den folgenden Schlüsseln:
- [states]: die vom Controller zurückgegebenen Zustände, die zu einer Weiterleitung führen;
- [to]: die Weiterleitungs-URL;
- Zeilen 7–12: Wir durchlaufen die Liste der Weiterleitungen;
- Zeile 9: Für jede Weiterleitung rufen wir die Status ab, die dazu führen;
- Zeile 10: Wenn der geprüfte Status in dieser Liste enthalten ist, führen wir die Weiterleitung aus (Zeile 12);
- Zeile 12: Beachten Sie, dass die Methode [build_http_response] ein Tupel mit zwei Elementen zurückgeben muss:
- [response]: die zu sendende HTTP-Antwort. Diese wird mithilfe der Funktion [redirect] erstellt, deren Parameter die Weiterleitungs-URL ist;
- [status_code]: der HTTP-Antwortstatuscode, hier der Code [status.HTTP_302_FOUND], der dem Client mitteilt, dass eine Weiterleitung erfolgen soll;
Führen wir einen [Postman]-Test durch. Wir:
- initialisieren eine HTML-Sitzung [init-session/html];
- authentifizieren [/authenticate-user];
- beenden die Sitzung [/end-session];

Die Antwort des Servers lautet wie folgt:

Wir haben die Authentifizierungsansicht erhalten. Das ist genau das, was wir erwartet haben. Schauen wir uns nun an, wie wir sie erhalten haben. Wechseln wir zur [Postman]-Konsole (Strg-Alt-C):

- in [1] die Aktion [/end-session];
- In [2-3] teilt der vom Server zurückgegebene HTTP-Statuscode 302 dem Client mit, dass eine Weiterleitung erfolgt;
- In [4] folgt der Client [Postman] der Weiterleitung;
32.10. Testen der HTML-Anwendung unter realen Bedingungen
Der Code wurde geschrieben und jede Aktion mit [Postman] getestet. Wir müssen den View-Flow noch in einem realen Szenario testen. Wir benötigen eine Möglichkeit, die HTML-Sitzung zu initialisieren. Wir wissen, dass wir die Anfrage [/init-session/html] an den Server senden müssen. Dies ist keine sehr praktische URL. Wir würden es vorziehen, mit der URL [/] zu beginnen.
Wir haben die folgende Route im Hauptskript [main] geschrieben:
- Zeilen 4–7: Behandlung der Route [/]. Der Einstiegspunkt für die Webanwendung ist die URL [/init-session/html] (Zeile 10). Außerdem leiten wir in Zeile 7 den Client auf diese URL um:
- Die Funktion [url_for] wird in Zeile 1 importiert. Sie hat hier zwei Parameter (Zeile 7):
- Der erste Parameter ist der Name einer der Routing-Funktionen, in diesem Fall die in Zeile 11. 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 11, [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 10 zurück, wobei der Parameter durch seinen Wert [/init-session/html] ersetzt wird;
- Die Funktion [redirect] wurde in Zeile 1 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;
Wir sind bereit. Sehen wir uns nun einige Ansichtssequenzen an.
In unserem Browser aktivieren wir die Entwicklertools (F12 in Chrome, Firefox, Edge) und rufen die Start-URL [http://localhost:5000/] auf. Die Antwort des Servers lautet wie folgt:

Wenn wir uns den Netzwerkverkehr zwischen dem Client und dem Server ansehen:

- sehen wir, dass der Browser bei [4, 5] eine Weiterleitungsanfrage an die URL [/init-session/html] erhalten hat;
Füllen wir das erhaltene Formular aus;

Führen wir dann ein paar Simulationen durch:


Fordern wir die Liste der Simulationen an:

Löschen wir die erste Simulation:

Beenden wir die Sitzung:

Die Leser werden dazu ermutigt, weitere Tests auszuprobieren.