32. De modus HTML van versie 12
Aan het begin van versie 12 hadden we aangegeven dat we de applicatie in verschillende fasen zouden ontwikkelen. We hadden geschreven:
- op basis van de weergaven van de applicatie HTML gaan we de acties definiëren die de webapplicatie moet implementeren. We gaan hier de daadwerkelijke weergaven gebruiken, maar het zouden ook gewoon weergaven op papier kunnen zijn;
- op basis van deze acties gaan we de service-URL van de applicatie HTML definiëren;
- we gaan deze URL-services implementeren met een server die jSON levert. Zo kunnen we de basis van de webserver definiëren zonder ons zorgen te hoeven maken over de te leveren HTML-pagina's. We zullen deze URL-services testen met Postman;
- vervolgens zullen we onze jSON-server testen met een consoleclient;
- zodra de jSON-server is gevalideerd, gaan we verder met het schrijven van de HTML-applicatie;
We hebben de servers jSON en XML operationeel. We kunnen nu verdergaan met de server HTML. We zullen zien dat deze server de volledige architectuur overneemt die is ontwikkeld voor de servers jSON / XML en daar het beheer van weergaven HTML aan toevoegt.
32.1. Architectuur MVC
We gaan het zogenaamde MVC-architectuurmodel (Model – View – Controller) als volgt implementeren:
De verwerking van een verzoek van een klant verloopt als volgt:
- 1 - verzoek
De aangevraagde URL'en zullen de vorm http://machine:port/action/param1/param2/… hebben. De [Contrôleur principal] zal een configuratiebestand gebruiken om het verzoek naar de juiste controller te „routeren“. Hiervoor gebruikt hij het veld [action] van de URL. De rest van de URL en [param1/param2/…] bestaat uit optionele parameters die naar de actie worden doorgestuurd. De C van MVC is hier de tekenreeks [Contrôleur principal, Contrôleur / Action]. Als geen enkele controller de gevraagde actie kan verwerken, zal de webserver antwoorden dat de gevraagde URL niet is gevonden.
- 2 - verwerking
- De geselecteerde actie [2a] kan gebruikmaken van de parameters parami die de actie [Contrôleur principal] aan haar heeft doorgegeven. Deze kunnen afkomstig zijn uit twee bronnen:
- het pad [/param1/param2/…] van de URL,
- van parameters die in de hoofdtekst van het verzoek van de klant zijn opgenomen;
- bij de verwerking van het verzoek van de gebruiker kan de actie de laag [métier] [2b] nodig hebben. Zodra het verzoek van de client is verwerkt, kan dit verschillende reacties oproepen. Een klassiek voorbeeld is:
- een foutmelding als het verzoek niet correct kon worden verwerkt;
- anders een bevestigingsreactie;
- de [Contrôleur / Action] stuurt zijn antwoord [2c] terug naar de hoofdcontroller, samen met een statuscode. Deze statuscodes geven op unieke wijze de status van de applicatie weer. Dit is ofwel een succescode, ofwel een foutcode;
- 3 - antwoord
- afhankelijk van of de client een antwoord jSON, XML of HTML heeft aangevraagd, zal [Contrôleur principal] het juiste antwoordtype [3a] instantiëren en deze vragen het antwoord naar de klant te verzenden. De [Contrôleur principal] zal zowel het antwoord als de statuscode doorgeven die zijn geleverd door de [Contrôleur / Action] die is uitgevoerd;
- als het gewenste antwoord van het type jSON of XML is, zal het geselecteerde antwoord het antwoord van [Contrôleur / Action] dat het heeft ontvangen opmaken en naar [3c] verzenden. De client die dit antwoord kan verwerken, kan een Python-consolescript zijn of een JavaScript-script dat op een pagina met de naam HTML staat;
- als het gewenste antwoord van het type HTML is, zal het geselecteerde antwoord [3b] een van de weergaven HTML of [Vuei] selecteren aan de hand van de statuscode die eraan is meegegeven. Dit is de V van MVC. Aan elke statuscode is één weergave gekoppeld. Deze weergave V geeft het antwoord weer van de [Contrôleur / Action] die is uitgevoerd. Het verwerkt de gegevens van dit antwoord met behulp van HTML, CSS en JavaScript. Deze gegevens worden het model van de weergave genoemd. Dit is de M van MVC. De client is dan meestal een browser;
32.2. De scriptstructuur van de server HTML

- in [1], de statische elementen van de server HTML;
- in [2-3], de weergaven V van de server HTML. De fragmenten [2] zijn herbruikbare elementen in de weergaven [3];
- in [4], een map die zal worden gebruikt voor het statisch testen van de weergaven;
- in [5], de map met de M-sjablonen van de V-weergaven, de M van MVC;
32.3. Overzicht van de weergaven
De webapplicatie HTML maakt gebruik van vier weergaven. De eerste weergave is de authenticatiepagina:
- de actie die naar deze eerste weergave leidt, is de actie [/init-session] [1];
- een klik op de knop [Valider] activeert de actie [/authentifier-utilisateur] met twee verzonden parameters [2-3];
Het scherm voor de belastingberekening:

- in [1], de actie [/authentifier-utilisateur] die deze weergave oproept;
- in [2]: het klikken op de knop [Valider] activeert de uitvoering van de actie [/calculer-impot] met drie doorgegeven parameters [2-5];
- Als je op de link [6] klikt, wordt de actie [/lister-simulations] zonder parameters geactiveerd;
- als je op de link [7] klikt, wordt de actie [/fin-session] zonder parameters geactiveerd;
Het derde scherm toont de simulaties die door de geauthenticeerde gebruiker zijn uitgevoerd:

- in [1], de actie [/lister-simulations] die naar dit scherm leidt;
- in [2]: een klik op de link [Supprimer] activeert de actie [/supprimer-simulation] met één parameter, namelijk het nummer van de simulatie die uit de lijst moet worden verwijderd;
- een klik op de link [3] activeert de actie [/afficher-calcul-impot] zonder parameters, waardoor het overzicht van de belastingberekening opnieuw wordt weergegeven;
- een klik op de link [4] activeert de actie [/fin-session] zonder parameters;
Het vierde scherm wordt het scherm met onverwachte fouten genoemd:
- in [1]: de gebruiker heeft zelf URL ingevoerd. In dit voorbeeld waren er echter geen simulaties. We krijgen dus de foutmelding [2]. We kennen deze foutmelding. We hadden deze al bij jSON / XML. We noemen dit type fout een onverwachte fout, omdat deze bij normaal gebruik van de applicatie niet kan optreden. Ze kunnen alleen optreden wanneer de gebruiker zelf de URL invoert;
- in geval van een onverwachte fout kun je via de links [3-5] terugkeren naar een van de drie andere weergaven;
Laten we de verschillende URL-services van de server jSON / XML nog eens op een rijtje zetten:
Actie | Rol | Uitvoeringscontext |
/init-session | Wordt gebruikt om het type (json, xml, html) van de gewenste antwoorden vast te leggen | Verzoek GET Kan op elk moment worden verzonden |
/authentifier-utilisateur | Geeft een gebruiker al dan niet toestemming om in te loggen | Verzoek POST. Het verzoek moet twee POST-parameters bevatten [user, password] Kan alleen worden verzonden als het sessietype (json, xml, html) bekend is |
/belasting-berekenen | Voert een simulatie van de belastingberekening uit | Verzoek POST. Het verzoek moet drie POST-parameters bevatten: [marié, enfants, salaire] Kan alleen worden verzonden als het sessietype (json, xml, html) bekend is en de gebruiker is geauthenticeerd |
/simulaties-weergeven | Vraagt om de lijst met simulaties die sinds het begin van de sessie zijn uitgevoerd | Verzoek GET. Kan alleen worden verzonden als het type van de sessie (json, xml, html) bekend is en de gebruiker is geauthenticeerd |
/simulatie-verwijderen/nummer | Verwijdert een simulatie uit de lijst met simulaties | Verzoek GET. Kan alleen worden verzonden als het type van de sessie (json, xml, html) bekend is en de gebruiker is geauthenticeerd |
/belastingberekening-weergeven | Geeft de pagina HTML met de belastingberekening weer | Verzoek GET. Kan alleen worden verzonden als het sessietype (json, xml, html) bekend is en de gebruiker is geauthenticeerd |
/einde-sessie | Beëindigt de simulatiesessie. | Technisch gezien wordt de oude websessie verwijderd en wordt er een nieuwe sessie aangemaakt Kan alleen worden verzonden als het type van de sessie (json, xml, html) bekend is en de gebruiker is geauthenticeerd |
Deze verschillende URL-services zullen ook worden gebruikt voor de HTML-server.
32.4. Configuratie van de weergaven
Een actie wordt verwerkt door een controller. Deze controller retourneert een tuple (resultaat, status_code) waarbij:
- [résultat] een woordenboek is met sleutels [action, état, réponse];
- [status_code] de statuscode is van het antwoord HTTP dat aan de client wordt teruggestuurd;
In een sessie HTML hangt de pagina die na een actie wordt weergegeven af van de statuscode die door de controller wordt teruggestuurd. Deze afhankelijkheid komt in de configuratie [config] als volgt tot uiting:
# de weergaven HTML en hun sjablonen zijn afhankelijk van de status die door de controller wordt geretourneerd
"views": [
{
# authenticatiescherm
"états": [
# /sessie-starten geslaagd
700,
# /authentificatie-gebruiker mislukt
201
],
"view_name": "views/vue-authentification.html",
"model_for_view": ModelForAuthentificationView()
},
{
# overzicht belastingberekening
"états": [
# /gebruiker-authenticeren geslaagd
200,
# /belasting-berekenen geslaagd
300,
# /belasting-berekenen mislukt
301,
# /belastingberekening-weergeven
800
],
"view_name": "views/vue-calcul-impot.html",
"model_for_view": ModelForCalculImpotView()
},
{
# overzicht van de lijst met simulaties
"états": [
# /simulaties-weergeven
500,
# /simulatie-verwijderen
600
],
"view_name": "views/vue-liste-simulations.html",
"model_for_view": ModelForListeSimulationsView()
}
],
# overzicht van onverwachte fouten
"view-erreurs": {
"view_name": "views/vue-erreurs.html",
"model_for_view": ModelForErreursView()
},
# omleidingen
"redirections": [
{
"états": [
400, # /sessie succesvol beëindigen
],
# omleiding naar
"to": "/init-session/html",
}
],
}
- regel 2-40: [views] is een lijst met weergaven. Laten we eens kijken naar de weergave op de regels 3-13:
- regel 11: de weergegeven weergave V;
- regel 12: de klasse-instantie die verantwoordelijk is voor het genereren van het model M van deze weergave;
- regels 5-10: de statussen die naar deze weergave leiden;
- regels 3-13: de authenticatieweergave;
- regels 14-28: de weergave voor de belastingberekening;
- regels 29-39: de weergave van de lijst met simulaties;
- regels 42-46: de weergave van onverwachte fouten;
- regels 49-57: sommige rapporten leiden via een omleiding naar een weergave. Dit is het geval bij rapport 400, dat overeenkomt met de succesvolle actie [/fin-session]. De klant moet dan worden omgeleid naar de actie [http://machine:port/chemin/init-session/html];
We presenteren nu de verschillende weergaven.
32.5. De authenticatiepagina

32.5.1. Overzicht van de weergave
De authenticatiepagina ziet er als volgt uit:

De weergave bestaat uit twee elementen die we fragmenten noemen:
- het fragment [1] wordt gegenereerd door het fragment [v-bandeau.html];
- het fragment [2] wordt gegenereerd door het fragment [v-authentification.html];
De authenticatiepagina wordt gegenereerd door de volgende pagina [vue-authentification.html]:
<!-- document HTML -->
<!doctype html>
<html lang="fr">
<head>
<!-- Vereiste metatags -->
<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">
<!-- banner -->
{% include "fragments/v-bandeau.html" %}
<!-- tweekolomslay-out -->
<div class="row">
<div class="col-md-9">
{% include "fragments/v-authentification.html" %}
</div>
</div>
<!-- bij fout - er wordt een foutmelding weergegeven -->
{% 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>
Opmerkingen
- regel 2: een document HTML begint met deze regel;
- regels 3-36: de pagina HTML is ingekapseld in de tags <html> </html>;
- regels 4-11: header (head) van het document HTML;
- regel 6: de tag <meta charset> geeft hier aan dat het document is gecodeerd in UTF-8;
- regel 7: de tag <meta name=’viewport’> stelt de initiële weergave van de viewport in: over de volledige breedte van het scherm waarop deze wordt weergegeven (width) op de oorspronkelijke grootte (initial-scale), zonder aanpassing aan een kleiner schermformaat (shrink-to-fit);
- regel 9: de tag <link rel=’stylesheet’> verwijst naar het bestand CSS dat het uiterlijk van de weergave bepaalt. We gebruiken hier het framework CSS Bootstrap 4.4.1 [https://getbootstrap.com/docs/4.0/getting-started/introduction/] ;
- regel 10: de tag <title> bepaalt de titel van de pagina:

- regels 13-35: de hoofdtekst van de webpagina is ingekapseld in de tags <body></body>;
- regels 14-34: de tag <div> bakent een sectie van de weergegeven pagina af. De in de weergave gebruikte attributen [class] verwijzen allemaal naar het Bootstrap-framework CSS. De tag <div class=’container’> (regel 14) bakent een Bootstrap-container af;
- regel 26: het fragment [v-bandeau.html] wordt opgenomen. Dit fragment genereert de koptekst [1] van de pagina. We zullen dit straks beschrijven;
- regels 18-22: de tag <div class=’row’> markeert een Bootstrap-rij. Deze rijen bestaan uit 12 kolommen;
- regel 19: de tag <div class=’col-md-9’> markeert een sectie van 9 kolommen;
- regel 20: hier wordt het fragment [v-authentification.html] opgenomen, dat het authenticatieformulier [2] van de pagina weergeeft. We zullen dit straks toelichten;
- regels 24-33: de code HTML in deze regels wordt alleen gebruikt als [modèle.error] de waarde True heeft. We gaan altijd als volgt te werk: het model van een weergave HTML wordt ingekapseld in een woordenboek [modèle];
- regels 24-33: de authenticatie mislukt als de gebruiker onjuiste inloggegevens invoert. In dat geval wordt de authenticatieweergave opnieuw weergegeven met een foutmelding. Het attribuut [modèle.error] geeft aan of deze foutmelding moet worden weergegeven;
- regels 27-30: baken een gebied met een roze achtergrond af (class="alert alert-danger") (regel 27);

- regel 28: een tekst;
- regel 29: de tag HTML <ul> (ongeordende lijst) geeft een lijst met opsommingstekens weer. Elk element van de lijst moet de syntaxis <li>element</li> hebben. Hier wordt de waarde van [modèle.erreurs] weergegeven. Deze waarde wordt gefilterd (aanwezigheid van |) door het filter [safe]. Standaard ‘neutraliseert’ Flask, wanneer een tekenreeks naar de browser moet worden verzonden, alle HTML-tags die erin zouden kunnen voorkomen, zodat de browser ze niet interpreteert. Maar soms wil men ze juist wel interpreteren. Dit is hier het geval wanneer de tekenreeks [modèle.erreurs] de tags HTML <li> en </li> bevat, die dienen om een element in de lijst af te bakenen. In dat geval gebruiken we het filter [safe], dat Flask aangeeft dat de weer te geven tekenreeks veilig is en dat het de tags HTML die het daar aantreft dus niet moet neutraliseren;
Laten we uit deze code de dynamische elementen onthouden die moeten worden gedefinieerd:
- [modèle.error]: om een foutmelding weer te geven;
- [modèle.erreurs]: een lijst (in de zin van HTML) met foutmeldingen;
32.5.2. Het fragment [v-bandeau.html]
Het fragment [v-bandeau.html] geeft de bovenste balk weer in alle weergaven van de webapplicatie:

De code van het fragment [v-bandeau.html] is als volgt:
<!-- 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>
Opmerkingen
- regels 2-13: de banner is ingekapseld in een Bootstrap-sectie van het type Jumbotron [<div class="jumbotron">]. Deze Bootstrap-klasse geeft de weergegeven inhoud een specifieke stijl om deze te laten opvallen;
- regels 3-12: een Bootstrap-rij;
- regels 4-6: een afbeelding [img] is in de eerste vier kolommen van de regel geplaatst;
- regel 5: de syntaxis:
maakt gebruik van de Flask-functie [url_for]. Hier is de waarde ervan de URL uit het bestand [images/logo.pg] in de map [static];
- regels 7-11: de overige 8 kolommen van de regel (ter herinnering: er zijn er in totaal 12) worden gebruikt om tekst (regel 9) in grote letters (<h1>, regels 8-10) weer te geven;
32.5.3. Het fragment [v-authentification.html]
Het fragment [v-authentification.html] toont het authenticatieformulier van de webapplicatie:

De code van het fragment [v-authentification.html] is als volgt:
<!-- formulier HTML - de waarden worden verzonden met de actie [authentifier-utilisateur] -->
<form method="post" action="/authentifier-utilisateur">
<!-- titel -->
<div class="alert alert-primary" role="alert">
<h4>Veuillez vous authentifier</h4>
</div>
<!-- Bootstrap-formulier -->
<fieldset class="form-group">
<!-- eerste regel -->
<div class="form-group row">
<!-- tekst -->
<label for="user" class="col-md-3 col-form-label">Nom d'utilisateur</label>
<div class="col-md-4">
<!-- tekstinvoerveld -->
<input type="text" class="form-control" id="user" name="user"
placeholder="Nom d'utilisateur" value="{{ modèle.login }}" required>
</div>
</div>
<!-- 2e regel -->
<div class="form-group row">
<!-- tekst -->
<label for="password" class="col-md-3 col-form-label">Mot de passe</label>
<!-- tekstinvoerveld -->
<div class="col-md-4">
<input type="password" class="form-control" id="password" name="password"
placeholder="Mot de passe" required>
</div>
</div>
<!-- knop van het type [submit] op een derde regel -->
<div class="form-group row">
<div class="col-md-2">
<button type="submit" class="btn btn-primary">Valider</button>
</div>
</div>
</fieldset>
</form>
Opmerkingen
- regels 2-39: de tag <form> omkadert een formulier HTML. Dit formulier heeft doorgaans de volgende kenmerken:
- het definieert invoervelden (tags <input> in de regels 17 en 27;
- er is een knop van het type [submit] (regel 34) die de ingevoerde waarden doorstuurt naar de URL die is opgegeven in het attribuut [action] van de tag [form] (regel 2). De methode HTTP die wordt gebruikt om deze URL op te roepen, wordt gespecificeerd in het attribuut [method] van de tag [form] (regel 2);
- wanneer de gebruiker hier op de knop [Valider] (regel 34) klikt, zal de browser (regel 2) de in het formulier ingevoerde waarden naar de URL [/authentifier-utilisateur] (regel 2) verzenden;
- de verzonden waarden zijn de waarden die de gebruiker heeft ingevoerd in de invoervelden van de regels 17 en 27. Ze worden verzonden in de body van het verzoek HTTP dat de browser zal uitvoeren in de vorm [x-www-forl-urlencoded]. De namen van de parameters [user, password] zijn die van de attributen [name] van de invoervelden op regel 17 en 27;
- regels 5-7: een Bootstrap-sectie om een titel op een blauwe achtergrond weer te geven:
- regels 10-37: een Bootstrap-formulier. Alle elementen van het formulier krijgen dan een bepaalde opmaak;
- regels 12-20: definiëren de eerste Bootstrap-regel van het formulier:
![]()
- regel 14 definieert het label [1] over drie kolommen. Het attribuut [for] van de tag [label] koppelt de tekst aan het attribuut [id] van het invoerveld op regel 17;
- regels 15-19: plaatst het invoerveld in een groep van vier kolommen;
- regels 17-18: de tag HTML [input] beschrijft een invoerveld. Deze heeft verschillende parameters:
- [type=’text’]: dit is een tekstinvoerveld. Hierin kan alles worden ingevoerd;
- [class=’form-control’]: Bootstrap-stijl voor het invoerveld;
- [id=’user’]: de ID van het invoerveld. Deze ID wordt doorgaans gebruikt door CSS en de JavaScript-code;
- [name=’user’]: naam van het invoerveld. Onder deze naam wordt de door de gebruiker ingevoerde waarde door de browser verzonden;
- [placeholder=’invite’]: de tekst die in het invoerveld wordt weergegeven wanneer de gebruiker nog niets heeft ingevoerd;
![]()
- (vervolg)
- [value=’valeur’]: de tekst ‘waarde’ wordt in het invoerveld weergegeven zodra dit wordt getoond, dus nog voordat de gebruiker iets invoert. Dit mechanisme wordt gebruikt om bij een fout de invoer weer te geven die de fout heeft veroorzaakt. In dit geval is deze waarde gelijk aan de waarde van de variabele [modèle.login];
- [required]: vereist dat de gebruiker een waarde invoert, zodat het formulier naar de server kan worden verzonden:
- regels 21-30: een soortgelijke code voor het invoeren van het wachtwoord;
- regel 27: [type=’password’] zorgt ervoor dat er een tekstinvoerveld is (je kunt alles invoeren), maar de ingevoerde tekens worden verborgen:
![]()
- regels 32-36: een derde Bootstrap-regel voor de knop [Valider];
- regel 34: omdat deze het attribuut [type=submit] heeft, zorgt een klik op deze knop ervoor dat de browser de ingevoerde waarden naar de server verstuurt, zoals eerder uitgelegd. Het attribuut CSS [class="btn btn-primary"] geeft een blauwe knop weer:
Er moet nog één ding worden uitgelegd. In regel 2 definieert het attribuut [action="/authentifier-utilisateur"] een onvolledige URL (deze begint niet met http://machine:port/chemin). In ons voorbeeld hebben alle URL-codes in de applicatie de vorm [http://machine:port/chemin/action/param1/param2/..], waarbij [http://machine:port/chemin] de basis is van de URL-codes voor de dienst. In [action="/authentifier-utilisateur"] hebben we een absolute URL, d.w.z. gemeten vanaf de root van de URL. De volledige URL van de POST is dus [http://machine:port/chemin/authentifier-utilisateur] en dat is wat de browser zal gebruiken.
We onthouden dat dit fragment het patroon [modèle.login] gebruikt.
32.5.4. Visuele tests
We kunnen de weergaven al ruim voordat ze in de applicatie worden geïntegreerd testen. Het gaat hier om het testen van hun visuele uiterlijk. We verzamelen alle testweergaven in de map [tests_views] van het project:

Om de weergave V [vue-authentification.html] te testen, moeten we het gegevensmodel M aanmaken dat deze weergave zal weergeven. Dit doen we met het script [test_vue_authentification.py]:
from flask import Flask, render_template, make_response
# Flask-applicatie
app = Flask(__name__, template_folder="../templates", static_folder="../static")
# Home URL
@app.route('/')
def index():
# de gegevens van de pagina worden in een sjabloon ingekapseld
modèle = {}
# gebruikers-ID
modèle["login"] = "albert"
# foutenlijst
modèle["error"] = True
erreurs = ["erreur1", "erreur2"]
# er wordt een lijst HTML met fouten samengesteld
content = ""
for erreur in erreurs:
content += f"<li>{erreur}</li>"
modèle["erreurs"] = content
# de pagina wordt weergegeven
return make_response(render_template("views/vue-authentification.html", modèle=modèle))
# main
if __name__ == '__main__':
app.config.update(ENV="development", DEBUG=True)
app.run()
Opmerkingen
- regels 1-3: we maken een Flask-applicatie aan met als enig doel de weergave [vue-authentification.html] weer te geven (regel 22);
- regel 7: de applicatie heeft slechts één service-URL;
- regels 9-20: de authenticatieweergave bevat dynamische onderdelen die worden aangestuurd door het object [modèle]. Dit object wordt het model van de weergave genoemd. Volgens een van de twee definities die voor de afkorting MVC zijn gegeven, is dit de M van MVC. Bij het definiëren van de weergave [vue-authentification.html] hadden we drie dynamische waarden geïdentificeerd:
- [modèle.error]: een booleaanse waarde die aangeeft of er een foutmelding moet worden weergegeven;
- [modèle.erreurs]: een lijst HTML met foutmeldingen;
- [modèle.login]: de gebruikersnaam van een gebruiker;
We moeten deze drie dynamische waarden dus definiëren.
- regels 9-20: we definiëren de drie dynamische elementen van het authenticatiescherm;
Om de test uit te voeren, starten we het script [tests_views/test_vue_authentification.py] en vragen we om de URL [/localhost:5000/]:
We zetten deze visuele tests voort totdat we tevreden zijn met het resultaat.

32.5.5. Berekening van het weergavemodel
Zodra het visuele aspect van de weergave is vastgesteld, kan het model van de weergave onder reële omstandigheden worden berekend. De modellen van de weergaven worden gegenereerd door klassen die zijn verzameld in de map [models_for_views]:

Elke klasse die een weergavemodel genereert, voldoet aan de volgende interface [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
- regels 8-10: de methode [get_model_for_view] is verantwoordelijk voor het genereren van een weergavesjabloon dat in een woordenboek is ingekapseld. Hiervoor ontvangt deze methode de volgende informatie:
- [request, session, config] zijn dezelfde parameters die door de controller van de actie worden gebruikt. Ze worden dus ook doorgegeven aan het model;
- de controller heeft een resultaat [résultat] gegenereerd dat eveneens naar het model wordt doorgegeven. Dit resultaat bevat een belangrijk element [état] dat aangeeft hoe de uitvoering van de huidige actie is verlopen. Het model zal deze informatie gebruiken;
We hebben gezien dat in de configuratie [config] van de applicatie de door de controllers geretourneerde statuscodes worden gebruikt om de weer te geven weergave HTML aan te duiden:
# de weergaven HTML en hun sjablonen zijn afhankelijk van de status die door de controller wordt geretourneerd
"views": [
{
# authenticatieweergave
"états": [
# /init-session geslaagd
700,
# /authentificatie-gebruiker mislukt
201
],
"view_name": "views/vue-authentification.html",
"model_for_view": ModelForAuthentificationView()
},
{
# weergave van de belastingberekening
"états": [
# /gebruikersauthenticatie geslaagd
200,
# /belasting-berekenen geslaagd
300,
# /belasting-berekenen mislukt
301,
# /belastingberekening-weergeven
800
],
"view_name": "views/vue-calcul-impot.html",
"model_for_view": ModelForCalculImpotView()
},
{
# overzicht van de lijst met simulaties
"états": [
# /simulaties-weergeven
500,
# /simulatie-verwijderen
600
],
"view_name": "views/vue-liste-simulations.html",
"model_for_view": ModelForListeSimulationsView()
}
],
# overzicht van onverwachte fouten
"view-erreurs": {
"view_name": "views/vue-erreurs.html",
"model_for_view": ModelForErreursView()
},
# omleidingen
"redirections": [
{
"états": [
400, # /sessie succesvol beëindigen
],
# omleiding naar
"to": "/init-session/html",
}
],
}
Het zijn dus de statuscodes [700, 201] (regels 7 en 9) die ervoor zorgen dat de authenticatieweergave wordt weergegeven. Om de betekenis van deze codes te achterhalen, kunnen we gebruikmaken van de tests [Postman] die zijn uitgevoerd op de applicatie jSON:
- [init-session-json-700]: 700 is de statuscode na een succesvolle [init-session]-actie: het authenticatieformulier wordt dan leeg weergegeven;
- [authentifier-utilisateur-201]: 201 is de statuscode na een mislukte actie [authentifier-utilisateur] (inloggegevens niet herkend): het authenticatieformulier wordt dan weergegeven zodat het kan worden gecorrigeerd;
Nu we weten wanneer het authenticatieformulier moet worden weergegeven, kunnen we het sjabloon ervan berekenen in [ModelForAuthentificationView] (regel 12):
from flask import Request
from werkzeug.local import LocalProxy
from InterfaceModelForView import InterfaceModelForView
class ModelForAuthentificationView(InterfaceModelForView):
def get_model_for_view(self, request: Request, session: LocalProxy, config: dict, résultat: dict) -> dict:
# de gegevens van de pagina worden in het sjabloon ingekapseld
modèle = {}
# status van de applicatie
état = résultat["état"]
# het sjabloon is afhankelijk van de status
if état == 700:
# in het geval dat het formulier leeg wordt weergegeven
modèle["login"] = ""
# er is geen foutmelding
modèle["error"] = False
elif état == 201:
# verificatie mislukt
# de oorspronkelijk ingevoerde gebruiker wordt opnieuw weergegeven
modèle["login"] = request.form.get("user")
# er is een fout te tonen
modèle["error"] = True
# lijst HTML met foutmeldingen
erreurs = ""
for erreur in résultat["réponse"]:
erreurs += f"<li>{erreur}</li>"
modèle["erreurs"] = erreurs
# het sjabloon wordt weergegeven
return modèle
Opmerkingen
- regel 8: de methode [get_model_for_view] van de authenticatieweergave moet een woordenboek met drie sleutels [error, erreurs, login] leveren. Deze berekening wordt uitgevoerd op basis van de statuscode die door de controller van de actie wordt geretourneerd;
- regel 12: de statuscode wordt opgehaald die is geretourneerd door de controller die de huidige actie heeft verwerkt;
- regels 14-29: het model is afhankelijk van deze statuscode;
- regels 15-18: het geval waarin een leeg authenticatieformulier moet worden weergegeven;
- regels 20-29: in het geval van een mislukte authenticatie: de door de gebruiker ingevoerde gebruikersnaam wordt weergegeven en er wordt een foutmelding getoond. De gebruiker kan dan via het toetsenbord een nieuwe authenticatiepoging doen;
- regel 22: de aanvankelijk door de gebruiker ingevoerde gebruikersnaam kan worden teruggevonden in het verzoek van de client;
- regel 24: er wordt gemeld dat er fouten moeten worden weergegeven;
- regels 26-29: in geval van een fout bevat het resultaat [‘réponse’] een lijst met fouten;
32.5.6. Genereren van antwoorden HTML
Laten we teruggaan naar het model MVC van de applicatie HTML:
- in 2 (2a, 2b): de controller voert een actie uit;
- in 3 (3a, 3b, 3c): er wordt een weergave gekozen en naar de client verzonden;
In [3a] wordt een responssoort (jSON, XML, HTML) gekozen. We hebben gezien hoe de antwoorden jSON en XML werden gegenereerd, maar nog niet de antwoorden HTML. Deze worden gegenereerd door de klasse [HtmlResponse]:

Laten we nog eens bekijken hoe in het hoofdscript [main] wordt bepaald welk type antwoord aan de gebruiker moet worden gegeven:
….
# het te verzenden antwoord wordt samengesteld
response_builder = config["responses"][type_response]
response, status_code = response_builder \
.build_http_response(request, session, config, status_code, résultat)
# het antwoord wordt verzonden
return response, status_code
waarbij, op regel 3, config[‘responses’] het volgende woordenboek is:
# de verschillende soorten antwoorden (json, xml, html)
"responses": {
"json": JsonResponse(),
"html": HtmlResponse(),
"xml": XmlResponse()
},
Het is dus de klasse [HtmlResponse] die het antwoord HTML genereert. De code daarvan is als volgt:
# woordenlijst met antwoorden HTML op basis van de status in het resultaat
from flask import make_response, render_template
from flask.wrappers import Response
from werkzeug.local import LocalProxy
from InterfaceResponse import InterfaceResponse
class HtmlResponse(InterfaceResponse):
def build_http_response(self, request: LocalProxy, session: LocalProxy, config: dict, status_code: int,
résultat: dict) -> (Response, int):
# het antwoord HTML is afhankelijk van de statuscode die door de controller wordt geretourneerd
état = résultat["état"]
# moet er een omleiding plaatsvinden?
for redirection in config["redirections"]:
# statussen waarbij een omleiding nodig is
états = redirection["états"]
if état in états:
# er moet een omleiding worden uitgevoerd
return redirect(f"/{redirection['to']}"), status.HTTP_302_FOUND
# aan elke status hoort een weergave
# deze wordt gezocht in de lijst met weergaven
views_configs = config["views"]
trouvé = False
i = 0
# de lijst met weergaven wordt doorlopen
nb_views = len(views_configs)
while not trouvé and i < nb_views:
# weergave nr. i
view_config = views_configs[i]
# rapporten die bij weergave nr. i horen
états = view_config["états"]
# bevindt de gezochte status zich onder de statussen die bij weergave nr. i horen
if état in états:
trouvé = True
else:
# volgende weergave
i += 1
# Gevonden?
if not trouvé:
# als er geen weergave bestaat voor de huidige status van de applicatie
# de foutweergave wordt weergegeven
view_config = config["view-erreurs"]
# het weergavemodel van de weer te geven weergave wordt berekend
model_for_view = view_config["model_for_view"]
modèle = model_for_view.get_model_for_view(request, session, config, résultat)
# de code van het antwoord wordt gegenereerd
html = render_template(view_config["view_name"], modèle=modèle)
# het antwoord wordt samengesteld HTTP
response = make_response(html)
response.headers['Content-Type'] = 'text/html; charset=utf-8'
# het resultaat wordt weergegeven
return response, status_code
- regel 11: de methode [build_http_response], die verantwoordelijk is voor het genereren van het antwoord HTML, ontvangt de volgende parameters:
- [request, session, dict]: dit zijn de parameters die door de controller worden gebruikt om de lopende actie te verwerken;
- [status_code, résultat] zijn de twee resultaten die door dezezelfde controller worden geproduceerd;
- regel 14: zoals gezegd hangt het antwoord HTML van de server af van de statuscode in het woordenboek [résultat];
- regels 16-22: eerst worden de omleidingen afgehandeld. Voorlopig laten we dit geval buiten beschouwing totdat we een voorbeeld van een omleiding tegenkomen. Merk op dat omleidingen typisch een gebruiksscenario zijn van de server HTML. Dit komt niet voor bij de servers jSON en ouXML;
- regels 24-41: we zoeken onder de weergaven naar degene waarvan de lijst [états] de gezochte status bevat;
- regels 42-46: als er geen weergave is gevonden, is er sprake van een onverwachte fout. Laten we een voorbeeld nemen. Bij normaal gebruik van de applicatie mag de actie [/supprimer-simulation] nooit een fout vertonen. We zullen namelijk zien dat het verwijderen van simulaties plaatsvindt op basis van links die door de code worden gegenereerd. Deze links zijn correct en kunnen niet tot een fout leiden. Zoals we echter hebben gezien, kan de gebruiker de codes URL en [/supprimer-simulation/id] rechtstreeks invoeren en zo een fout veroorzaken. In dat geval retourneert de controller [SupprimerSimulationController] een statuscode 601. Deze statuscode staat echter niet in de lijst met statuscodes die leiden tot de weergave van een pagina HTML. Er wordt dan ook de foutpagina weergegeven. Deze is als volgt gedefinieerd in de configuratie:
# overzicht van onverwachte fouten
"view-erreurs": {
"view_name": "views/vue-erreurs.html",
"model_for_view": ModelForErreursView()
},
- regel 49: zodra bekend is welke weergave moet worden getoond, wordt de klasse opgehaald die het bijbehorende model genereert. Ook deze staat in de configuratie [config];
- regel 50: zodra deze klasse is gevonden, genereren we het model van de weergave;
- regel 52: zodra het model M van de weergave V is berekend, kan de code HTML van de weergave worden gegenereerd;
- regels 54-55: het antwoord HTTP wordt opgebouwd met een hoofdtekst HTML;
- regels 56-57: het antwoord HTTP wordt weergegeven met de bijbehorende statuscode;
32.5.7. Tests [Postman]
We gaan verzoeken uitvoeren die de codes [700, 201] opleveren, die het authenticatiescherm weergeven:
- [init-session-html-700]: 700 is de statuscode na een succesvolle actie [init-session]: vervolgens wordt het lege authenticatieformulier weergegeven;
- [authentifier-utilisateur-201]: 201 is de statuscode na een mislukte actie [authentifier-utilisateur] (inloggegevens niet herkend): het authenticatieformulier wordt dan weergegeven zodat het kan worden gecorrigeerd;
Je hoeft ze alleen maar opnieuw te gebruiken en te kijken of ze de authenticatiepagina correct weergeven. Hier worden twee gevallen getoond:
Geval 1: [init-session-html-700], begin van een sessie HTML;

Het antwoord is als volgt:

- in [5] maakt de modus [Preview] het mogelijk om de ontvangen pagina HTML te bekijken;
- in [6] zien we inderdaad het verwachte lege formulier;
- in [7] heeft Postman de link naar de afbeelding op de pagina niet gevolgd;
- in [8] geeft de modus [Raw] toegang tot het ontvangen HTML;

- in [3]: de link die Postman niet heeft geladen. Het heeft de waarde van het attribuut [alt=alternative] weergegeven, die te zien is wanneer de afbeelding niet kan worden geladen. In dit geval is het eerder zo dat Postman de afbeelding niet wilde laden. Dit kan worden gecontroleerd door de URL en [http://localhost :5000/static/images.logo.jpg] op te vragen met Postman:
Geval 2: [authentifier-utilisateur-201], onjuiste authenticatie

Laten we nu een foutieve authenticatie uitvoeren, nadat we een succesvolle sessie-initialisatie met HTML hebben uitgevoerd:

Hierboven:
- in [4,7]: het verzoek verstuurt de tekenreeks [user=bernard&password=thibault];
Het antwoord is als volgt:

- in [4] wordt een foutmelding weergegeven;
- in [3] werd de verkeerde gebruiker opnieuw weergegeven;
32.5.8. Conclusie
We hebben de weergave [vue-authentification.html] kunnen testen zonder de andere weergaven te hebben geschreven. Dit was mogelijk omdat:
- alle controllers zijn geschreven;
- [Postman] stelt ons in staat om verzoeken naar de server te sturen zonder dat alle weergaven nodig zijn. Bij het schrijven van controllers moet men erop voorbereid zijn om verzoeken te verwerken die door geen enkele weergave zouden worden ondersteund. Men mag nooit bij voorbaat denken: “dit verzoek is onmogelijk”. Men moet het controleren;
32.6. De weergave voor de belastingberekening

32.6.1. Overzicht van de weergave
De weergave voor de belastingberekening ziet er als volgt uit:

Het scherm bestaat uit drie delen:
- 1: de bovenste balk wordt gegenereerd door het fragment [v-bandeau.html] dat al is getoond;
- 2: het formulier voor de belastingberekening, gegenereerd door het fragment [v-calcul-impot.html];
- 3: een menu met twee links, gegenereerd door het fragment [v-menu.html];
Het scherm voor de belastingberekening wordt gegenereerd door de volgende code [vue-calcul-impot.html]:
<!-- document HTML -->
<!doctype html>
<html lang="fr">
<head>
<!-- Vereiste metatags -->
<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">
<!-- banner -->
{% include "fragments/v-bandeau.html" %}
<!-- tweekolomslay-out -->
<div class="row">
<!-- het menu -->
<div class="col-md-3">
{% include "fragments/v-menu.html" %}
</div>
<!-- het berekeningsformulier -->
<div class="col-md-9">
{% include "fragments/v-calcul-impot.html" %}
</div>
</div>
<!-- succesmelding -->
{% if modèle.success %}
<!-- er wordt een melding van succes weergegeven -->
<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 %}
<!-- foutenlijst met 9 kolommen -->
<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>
Opmerkingen
- we geven alleen commentaar op nieuwe elementen die we nog niet zijn tegengekomen;
- regel 16: opname van de bovenste balk van het scherm in de eerste Bootstrap-regel van het scherm;
- regel 21: opname van het menu dat drie kolommen van de tweede Bootstrap-regel van de weergave zal innemen (regels 18, 20);
- regel 25: opname van het formulier voor de belastingberekening dat negen kolommen (regel 24) van de tweede Bootstrap-regel van de weergave (regel 18) in beslag zal nemen;
- regels 30-46: als de belastingberekening slaagt ([modèle.success=True]), wordt het resultaat van de belastingberekening weergegeven in een groen kader (regels 37-43). Dit kader bevindt zich in de derde Bootstrap-rij van de weergave (regel 32) en beslaat negen kolommen (regel 36) rechts van drie lege kolommen (regels 33-35). Dit kader komt dus onder het formulier voor de belastingberekening te staan;
- regels 48-61: als de belastingberekening mislukt ([modèle.error=True]), wordt er een foutmelding weergegeven in een roze kader (regels 55-58). Dit kader bevindt zich in de derde Bootstrap-regel van de weergave (regel 50) en beslaat negen kolommen (regel 54) rechts van drie lege kolommen (regels 51-53). Dit kader komt dus ook onder het formulier voor de belastingberekening te staan;
32.6.2. Het fragment [v-calcul-impot.html]
Het fragment [v-calcul-impot.html] geeft het formulier voor de belastingberekening van de webapplicatie weer:
De code van het fragment [v-calcul-impot.html] is als volgt:

<!-- formulier HTML verzonden -->
<form method="post" action="/calculer-impot">
<!-- bericht in 12 kolommen op een blauwe achtergrond -->
<div class="col-md-12">
<div class="alert alert-primary" role="alert">
<h4>Remplissez le formulaire ci-dessous puis validez-le</h4>
</div>
</div>
<!-- formulieronderdelen -->
<fieldset class="form-group">
<!-- eerste regel over 9 kolommen -->
<div class="row">
<!-- tekst over 4 kolommen -->
<legend class="col-form-label col-md-4 pt-0">Etes-vous marié(e) ou pacsé(e)?</legend>
<!-- keuzerondjes over 5 kolommen-->
<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>
<!-- tweede rij met 9 kolommen -->
<div class="form-group row">
<!-- tekst over 4 kolommen -->
<label for="enfants" class="col-md-4 col-form-label">Nombre d'enfants à charge</label>
<!-- numeriek invoerveld voor het aantal kinderen in 5 kolommen -->
<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>
<!-- derde regel met 9 kolommen -->
<div class="form-group row">
<!-- tekst in 4 kolommen -->
<label for="salaire" class="col-md-4 col-form-label">Salaire annuel net imposable</label>
<!-- veld voor het invoeren van het salaris, verdeeld over 5 kolommen -->
<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>
<!-- vierde regel, knop [submit] over 5 kolommen -->
<div class="form-group row">
<div class="col-md-5">
<button type="submit" class="btn btn-primary">Valider</button>
</div>
</div>
</fieldset>
</form>
Opmerkingen
- regel 2: het formulier HTML wordt verzonden (attribuut [method]) naar URL [/calculer-impot] (attribuut [action]). De verzonden waarden zijn de waarden van de invoervelden:
- de waarde van de aangevinkte keuzeknop in de vorm:
- [marié=oui] als het keuzerondje [Oui] is aangevinkt (regels 17-22). [marié] is de waarde van het attribuut [name] van regel 18, [oui] de waarde van het attribuut [value] van regel 18;
- [marié=non] als het keuzerondje [Non] is aangevinkt (regels 23-28). [marié] is de waarde van het attribuut [name] van regel 24, [non] de waarde van het attribuut [value] van regel 24;
- de waarde van het numerieke invoerveld op regel 37 in de vorm [enfants=xx], waarbij [enfants] de waarde is van het attribuut [name] op regel 37, en [xx] de waarde die de gebruiker via het toetsenbord heeft ingevoerd;
- de waarde van het numerieke invoerveld op regel 46 in de vorm [salaire=xx], waarbij [salaire] de waarde is van het attribuut [name] op regel 46, en [xx] de waarde die de gebruiker via het toetsenbord heeft ingevoerd;
Uiteindelijk zal de verzonden waarde de vorm [marié=xx&enfants=yy&salaire=zz] hebben.
- (vervolg)
- de ingevoerde waarden worden verzonden wanneer de gebruiker op de knop van het type [submit] op regel 53 klikt;
- regels 16-30: de twee keuzerondjes:
![]()
De twee keuzerondjes maken deel uit van dezelfde groep keuzerondjes omdat ze hetzelfde attribuut [name] hebben (regels 18, 24). De browser zorgt ervoor dat binnen een groep keuzerondjes er op elk moment slechts één is aangevinkt. Als je dus op één ervan klikt, wordt het keuzerondje dat eerder was aangevinkt gedeactiveerd;
- het zijn keuzerondjes vanwege het attribuut [type="radio"] (regels 18, 24);
- bij het weergeven van het formulier (vóór het invoeren) moet één van de keuzerondjes geselecteerd zijn: hiervoor volstaat het om het attribuut [checked=’checked’] toe te voegen aan de betreffende <input type="radio">-tag. Dit wordt gerealiseerd met dynamische variabelen:
- [modèle.checkedOui] op regel 18;
- [modèle->checkedNon] op regel 24;
Deze variabelen maken deel uit van het sjabloon van de weergave.
- regel 37: een numeriek invoerveld [type="number"] met een minimumwaarde van 0 [min="0"]. In recente browsers betekent dit dat de gebruiker alleen een getal >=0 kan invoeren. In diezelfde recente browsers kan de invoer plaatsvinden via een schuifbalk die omhoog of omlaag kan worden geschoven. Het attribuut [step="1"] op regel 37 geeft aan dat de schuifregelaar in stappen van 1 eenheid werkt. Dit heeft tot gevolg dat de schuifregelaar alleen gehele getallen aanneemt die oplopen van 0 tot n met een stap van 1. Voor handmatige invoer betekent dit dat getallen met een komma niet worden geaccepteerd;
- regel 37: bij bepaalde weergaven moet het invoerveld voor de kinderen vooraf worden ingevuld met de laatste invoer die in dit veld is gedaan. Hiervoor wordt het attribuut [value] gebruikt, dat de weer te geven waarde in het invoerveld vastlegt. Deze waarde is dynamisch en wordt gegenereerd door de variabele [modèle.enfants];
- regel 37: het attribuut [required] dwingt de gebruiker om gegevens in te voeren, zodat het formulier kan worden gevalideerd;
- regel 46: dezelfde uitleg geldt voor het invoeren van het salaris als voor dat van de kinderen;
- regel 53: de knop van het type [submit] die de POST activeert op basis van de waarden die zijn ingevoerd in de URL en [/calculer-impot] (regel 2);
![]()
32.6.3. Het fragment [v-menu.html]
Dit fragment toont een menu links van het formulier voor de belastingberekening:

De code van dit fragment is als volgt:
<!-- Bootstrap-menu -->
<nav class="nav flex-column">
<!-- weergave van een lijst met links HTML -->
{% for optionMenu in modèle.optionsMenu %}
<a class="nav-link" href="{{optionMenu.url}}">{{optionMenu.text}}</a>
{% endfor %}
</nav>
Opmerkingen
- regels 2-7: de tag HTML [nav] omkadert een deel van het document HTML met navigatielinks naar andere documenten;
- regel 5: de tag HTML [a] introduceert een navigatielink:
- [optionMenu.url]: is de URL waarnaar wordt genavigeerd wanneer op de link [optionMenu.text] wordt geklikt. Dit is dan een bewerking [GET optionMenu.url] die door de browser wordt uitgevoerd. [optionMenu.url] is een absolute URL, gemeten vanaf de hoofdmap [http://machine :port/chemin] van de applicatie. In [1] wordt dus de link aangemaakt:
- regel 5: het sjabloon [modèle.optionsMenu] van het fragment zal een lijst zijn in de vorm:
- regels 2, 7: de klassen CSS en [nav, flex-column, nav-link] zijn Bootstrap-klassen die het uiterlijk van het menu bepalen;
32.6.4. Visuele test
We verzamelen deze verschillende elementen in de map [Tests] en maken een testsjabloon voor de weergave [vue-calcul-impot.html]:

Het testscript [test_vue_calcul_impot] ziet er als volgt uit:
from flask import Flask, render_template, make_response
# Flask-applicatie
app = Flask(__name__, template_folder="../templates", static_folder="../static")
# Home URL
@app.route('/')
def index():
# de gegevens van de pagina worden in een sjabloon ingekapseld
modèle = {}
# formulier
modèle["checkedOui"] = ""
modèle["checkedNon"] = 'checked="checked"'
modèle["enfants"] = 2
modèle["salaire"] = 300000
# succesbericht
modèle["success"] = True
modèle["impôt"] = "Montant de l'impôt : 1000 euros"
modèle["décôte"] = "Décôte : 15 euros"
modèle["réduction"] = "Réduction : 20 euros"
modèle["surcôte"] = "Surcôte : 0 euros"
modèle["taux"] = "Taux d'imposition : 14 %"
# foutmelding
modèle["error"] = True
erreurs = ["erreur1", "erreur2"]
# er wordt een lijst HTML met fouten samengesteld
content = ""
for erreur in erreurs:
content += f"<li>{erreur}</li>"
modèle["erreurs"] = content
# menu
modèle["optionsMenu"] = [
{"text": 'Liste des simulations', "url": '/lister-simulations'},
{"text": 'Fin de session', "url": '/fin-session'}]
# pagina wordt weergegeven
return make_response(render_template("views/vue-calcul-impot.html", modèle=modèle))
# hoofdmenu
if __name__ == '__main__':
app.config.update(ENV="development", DEBUG=True)
app.run()
Opmerkingen
- regels 9-34: we initialiseren alle dynamische onderdelen van de weergave [vue-calcul-impot.html] en de fragmenten [v-calcul-impot.html] en [v-menu.html];
- regel 36: de weergave [vue-calcul-impot.html] wordt weergegeven;
Wanneer we het testscript [test_vue_calcul_impot] uitvoeren, krijgen we het volgende resultaat:
We werken aan deze weergave totdat het visuele resultaat ons bevalt. Vervolgens kunnen we overgaan tot de integratie van de weergave in de webapplicatie die we aan het schrijven zijn.

32.6.5. Berekening van het model van de weergave
Zodra het visuele aspect van de weergave is vastgesteld, kunnen we overgaan tot het berekenen van het model van de weergave onder reële omstandigheden. Laten we nog eens kijken naar de statuscodes die naar deze weergave leiden. Deze zijn te vinden in het configuratiebestand:
{
# overzicht van de belastingberekening
"états": [
# /gebruiker-authenticeren geslaagd
200,
# /belasting-berekenen geslaagd
300,
# /belasting-berekenen mislukt
301,
# /belastingberekening-weergeven
800
],
"view_name": "views/vue-calcul-impot.html",
"model_for_view": ModelForCalculImpotView()
},
Het zijn dus de statuscodes [200, 300, 301, 800] die ervoor zorgen dat de weergave voor de belastingberekening wordt getoond. Om de betekenis van deze codes te achterhalen, kunnen we gebruikmaken van de tests [Postman] die zijn uitgevoerd op de applicatie jSON:
- [authentifier-utilisateur-200]: 200 is de statuscode na een succesvolle uitvoering van actie [authentifier-utilisateur]: het formulier voor de belastingberekening wordt dan leeg weergegeven;
- [calculer-impot-300]: 300 is de statuscode na een succesvolle uitvoering van de actie [calculer-impot]. Vervolgens wordt het berekeningsformulier weergegeven met de ingevoerde gegevens en het belastingbedrag. De gebruiker kan dan een nieuwe berekening uitvoeren;
- de statuscode [301] is de code die wordt verkregen bij een foutieve belastingberekening;
- de statuscode [800] zal later worden weergegeven. We zijn deze nog niet tegengekomen;
Nu we weten wanneer het formulier voor de belastingberekening moet worden weergegeven, kunnen we het sjabloon ervan berekenen in de klasse [ModelForCalculImpotView]:

from flask import Request
from werkzeug.local import LocalProxy
from InterfaceModelForView import InterfaceModelForView
class ModelForCalculImpotView(InterfaceModelForView):
def get_model_for_view(self, request: Request, session: LocalProxy, config: dict, résultat: dict) -> dict:
# de gegevens van de weergave worden in het sjabloon ingekapseld
modèle = {}
# status van de applicatie
état = résultat["état"]
# het model is afhankelijk van de status
if état in [200, 800]:
# eerste weergave van een leeg formulier
modèle["success"] = False
modèle["error"] = False
modèle["checkedNon"] = 'checked="checked"'
modèle["checkedOui"] = ""
modèle["enfants"] = ""
modèle["salaire"] = ""
elif état == 300:
# berekening geslaagd – weergave van het resultaat
modèle["success"] = True
modèle["error"] = False
modèle["impôt"] = f"Montant de l'impôt : {résultat['réponse']['impôt']} euros"
modèle["décôte"] = f'Décôte : {résultat["réponse"]["décôte"]} euros'
modèle["réduction"] = f"Réduction : {résultat['réponse']['réduction']} euros"
modèle["surcôte"] = f'Surcôte : {résultat["réponse"]["surcôte"]} euros'
modèle["taux"] = f"Taux d'imposition : {résultat['réponse']['taux'] * 100} %"
# formulier hersteld met de ingevoerde waarden
modèle["checkedOui"] = 'checked="checked"' if request.form.get("marié") == "oui" else ""
modèle["checkedNon"] = 'checked="checked"' if request.form.get("marié") == "non" else ""
modèle["enfants"] = request.form.get("enfants")
modèle["salaire"] = request.form.get("salaire")
elif état == 301:
# fout opgetreden – formulier hersteld met de ingevoerde waarden
modèle["checkedOui"] = 'checked="checked"' if request.form.get("marié") == "oui" else ""
modèle["checkedNon"] = 'checked="checked"' if request.form.get("marié") == "non" else ""
modèle["enfants"] = request.form.get("enfants")
modèle["salaire"] = request.form.get("salaire")
# fout
modèle["success"] = False
modèle["error"] = True
modèle["erreurs"] = ""
for erreur in résultat['réponse']:
modèle['erreurs'] += f"<li>{erreur}</li>"
# menuopties
modèle["optionsMenu"] = [
{"text": 'Liste des simulations', "url": '/lister-simulations'},
{"text": 'Fin de session', "url": '/fin-session'}]
# het sjabloon wordt weergegeven
return modèle
Opmerkingen
- regel 12: de weer te geven weergave hangt af van de statuscode die door de controller wordt geretourneerd;
- regels 14-21: weergave van een leeg formulier;
- regels 22-35: geval waarin de belastingberekening is geslaagd. De ingevoerde waarden en het belastingbedrag worden opnieuw weergegeven;
- regels 36-47: geval waarin de belastingberekening mislukt;
- regels 49-52: berekening van de twee menuopties;
32.6.6. Tests [Postman]
We initialiseren een sessie HTML met de aanvraag [init-session-html-700] en authenticeren ons vervolgens met de aanvraag [authentifier-utilisateur-200]. Vervolgens gebruiken we de volgende aanvraag [calculer-impot-300]:
Het antwoord van de server is als volgt:


Laten we nu de volgende aanvraag [calculer-impot-301] proberen:

Het antwoord van de server is als volgt:
Laten we nu eens een onverwacht geval bekijken, namelijk wanneer er parameters ontbreken in POST. Dit geval is niet mogelijk bij normaal gebruik van de applicatie. Maar iedereen kan een verzoek zoals HTTP ‘in elkaar knutselen’, zoals we nu doen:


- in [6] hebben we de verzonden parameter [marié] uitgeschakeld;
Het antwoord van de server is als volgt:

- in [3], de foutmelding van de server;
In deze applicatie hadden we de keuze. We konden aan deze fout een statuscode toekennen die de pagina met onverwachte fouten weergeeft. We hebben in deze applicatie voor elke controller twee statuscodes gekozen:
- [xx0]: bij succes;
- [xx1]: bij een mislukking;
Voor mislukkingen kunnen we de statuscodes diversifiëren om fouten nauwkeuriger te beheren. We hadden bijvoorbeeld kunnen kiezen voor:
- [xx1]: voor fouten die moeten worden weergegeven op de pagina die de fout heeft veroorzaakt;
- [xx2]: voor onverwachte fouten bij normaal gebruik van de applicatie;
32.7. De weergave van de lijst met simulaties

32.7.1. Overzicht van de weergave
De weergave met de lijst van simulaties ziet er als volgt uit:

De weergave die wordt gegenereerd door de code [vue-liste-simulations.html] bestaat uit drie delen:
- 1: de bovenste balk wordt gegenereerd door het fragment [v-bandeau.html] dat al is getoond;
- 3: de tabel met simulaties, gegenereerd door het fragment [v-liste-simulations.html];
- 2: een menu met twee links, gegenereerd door het reeds gepresenteerde fragment [v-menu.html];
De weergave van de simulaties wordt gegenereerd door de volgende code [vue-liste-simulations.html]:
<!-- document HTML -->
<!doctype html>
<html lang="fr">
<head>
<!-- Vereiste metatags -->
<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">
<!-- banner -->
{% include "fragments/v-bandeau.html" %}
<!-- tweekolomslay-out -->
<div class="row">
<!-- menu met drie kolommen-->
<div class="col-md-3">
{% include "fragments/v-menu.html" %}
</div>
<!-- lijst met simulaties in 9 kolommen-->
<div class="col-md-9">
{% include "fragments/v-liste-simulations.html" %}
</div>
</div>
</div>
</body>
</html>
Opmerkingen
- regel 16: opname van de koptekst van de applicatie [1];
- regel 21: toevoeging van het menu [2]. Dit wordt in drie kolommen onder de banner weergegeven;
- regel 26: opname van de simulatietabel [3]. Deze wordt weergegeven in negen kolommen onder de banner en rechts van het menu;
We hebben al twee van de drie fragmenten van dit scherm besproken:
Het fragment [v-liste-simulations.html] luidt als volgt:
{% if modèle.simulations is undefined or modèle.simulations|length==0 %}
<!-- bericht op blauwe achtergrond -->
<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 %}
<!-- bericht op blauwe achtergrond -->
<div class="alert alert-primary" role="alert">
<h4>Liste de vos simulations</h4>
</div>
<!-- tabel met simulaties -->
<table class="table table-sm table-hover table-striped">
<!-- kopteksten van de zes kolommen van de tabel -->
<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>
<!-- hoofdgedeelte van de tabel (weergegeven gegevens) -->
<tbody>
<!-- elke simulatie wordt weergegeven door de simulatietabel te doorlopen -->
{% for simulation in modèle.simulations %}
<!-- weergave van een rij uit de tabel met 6 kolommen - tag <tr> -->
<!-- kolom 1: rijkop (simulatienummer) – tag <th scope='row' -->
<!-- kolom 2: parameterwaarde [marié] - tag <td> -->
<!-- kolom 3: parameterwaarde [enfants] - tag <td> -->
<!-- kolom 4: parameterwaarde [salaire] - tag <td> -->
<!-- kolom 5: parameterwaarde [impôt] (van de belasting) - tag <td> -->
<!-- kolom 6: parameterwaarde [surcôte] - tag <td> -->
<!-- kolom 7: parameterwaarde [décôte] - tag <td> -->
<!-- kolom 8: parameterwaarde [réduction] - tag <td> -->
<!-- kolom 9: parameterwaarde [taux] (van de belasting) - tag <td> -->
<!-- kolom 10: link om de simulatie te verwijderen - tag <td> -->
<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 %}
Opmerkingen
- een tabel HTML is gemaakt met de tag <table> (regels 15 en 62);
- de kolomkoppen van de tabel worden gedefinieerd binnen een <thead>-tag (table head, regels 17, 30). De <tr>-tag (table row, regels 18 en 29) bakent een rij af. In de regels 19-28 definieert de <th>-tag (table header) een kolomkop. Er zijn er dus tien. [scope="col"] geeft aan dat de koptekst van toepassing is op de kolom. [scope="row"] geeft aan dat de koptekst van toepassing is op de rij;
- regels 32-61: de tag <tbody> omkadert de gegevens die door de tabel worden weergegeven;
- regels 47-58: de tag <tr> omkadert een rij van de tabel;
- regel 48: de tag <th scope=’row’> definieert de koptekst van de rij. De browser markeert deze koptekst;
- regels 49-57: elke tag <td> (tabelgegevens) definieert een kolom van de rij;
- regel 34: de lijst met simulaties is te vinden in het sjabloon [modèle.simulations], dat een lijst met woordenboeken is;
- regel 57: een link om de simulatie te verwijderen. Het model URL gebruikt het nummer van de simulatie die in de regel wordt weergegeven;
32.7.2. Visuele test
We maken een testscript voor de weergave [vue-liste-simulations.html]:
Het script [test_vue_liste_simulations] is als volgt:

from flask import Flask, make_response, render_template
# Flask-applicatie
app = Flask(__name__, template_folder="../templates", static_folder="../static")
# Home URL
@app.route('/')
def index():
# de gegevens van de pagina worden in een sjabloon ingekapseld
modèle = {}
# de simulaties worden in het door de pagina verwachte formaat weergegeven
modèle["simulations"] = [
{
"id": 7,
"marié": "oui",
"enfants": 2,
"salaire": 60000,
"impôt": 448,
"décôte": 100,
"réduction": 20,
"surcôte": 0,
"taux": 0.14
},
{
"id": 19,
"marié": "non",
"enfants": 2,
"salaire": 200000,
"impôt": 25600,
"décôte": 0,
"réduction": 0,
"surcôte": 8400,
"taux": 0.45
}
]
# menu
modèle["optionsMenu"] = [
{"text": "Calcul de l'impôt", "url": '/afficher-calcul-impot'},
{"text": 'Fin de session', "url": '/fin-session'}]
# de pagina wordt weergegeven
return make_response(render_template("views/vue-liste-simulations.html", modèle=modèle))
# hoofdpagina
if __name__ == '__main__':
app.config.update(ENV="development", DEBUG=True)
app.run()
Opmerkingen
- regels 12-35: we voegen twee simulaties toe aan het model
- regels 37-39: de tabel met menuopties;
Laten we deze weergave bekijken door dit script uit te voeren. We krijgen het volgende resultaat:

We werken aan deze weergave totdat we tevreden zijn met het visuele resultaat. Vervolgens kunnen we overgaan tot de integratie van de weergave in de webapplicatie die we aan het schrijven zijn.
32.7.3. Berekening van het model van de weergave
Zodra het visuele aspect van de weergave is vastgesteld, kunnen we overgaan tot het berekenen van het model van de weergave onder reële omstandigheden. Laten we nog eens kijken naar de statuscodes die naar deze weergave leiden. Deze zijn te vinden in het configuratiebestand:

{
# weergave van de lijst met simulaties
"états": [
# /simulaties-weergeven
500,
# /simulatie-verwijderen
600
],
"view_name": "views/vue-liste-simulations.html",
"model_for_view": ModelForListeSimulationsView()
}
Het zijn dus de statuscodes [500, 600] die ervoor zorgen dat de weergave van de simulaties wordt getoond. Om de betekenis van deze codes te achterhalen, kunnen we gebruikmaken van de tests [Postman] die zijn uitgevoerd op de applicatie jSON:
- [lister-simulations-500]: 500 is de statuscode na een geslaagde actie [lister-simulations]: vervolgens wordt de lijst met door de gebruiker uitgevoerde simulaties weergegeven;
- [supprimer-simulation-600]: 600 is de statuscode na een succesvolle uitvoering van de actie [supprimer-simulation]. Vervolgens wordt de nieuwe lijst met simulaties weergegeven die na deze verwijdering is verkregen;
Nu we weten op welke momenten de lijst met simulaties moet worden weergegeven, kunnen we het model ervan berekenen in de klasse [ModelForListeSimulationsView]:
from flask import Request
from werkzeug.local import LocalProxy
from InterfaceModelForView import InterfaceModelForView
class ModelForListeSimulationsView(InterfaceModelForView):
def get_model_for_view(self, request: Request, session: LocalProxy, config: dict, résultat: dict) -> dict:
# de gegevens van de pagina worden in een sjabloon ingekapseld
modèle = {}
# de simulaties worden gevonden in het antwoord van de controller die de actie heeft uitgevoerd
# in de vorm van een array van woordenboeken TaxPayer
modèle["simulations"] = résultat["réponse"]
# menu
modèle["optionsMenu"] = [
{"text": "Calcul de l'impôt", "url": '/afficher-calcul-impot'},
{"text": 'Fin de session', "url": '/fin-session'}]
# het sjabloon wordt weergegeven
return modèle
Opmerkingen
- regel 13: de weer te geven simulaties zijn te vinden in [résultat["réponse"]];
- regels 15-17: de weer te geven menuopties;
32.7.4. Tests [Postman]
Er
- een sessie HTML wordt geïnitialiseerd;
- wordt geauthenticeerd;
- voeren drie belastingberekeningen uit;
Met de test [lister-simulations-500] krijgen we de statuscode 500. Deze komt overeen met een verzoek om de simulaties te bekijken:

Het antwoord van de server is als volgt:

Met de test [supprimer-simulation-600] krijgen we statuscode 600. Hier gaan we simulatie nr. 2 verwijderen.
Het teruggestuurde resultaat is een lijst met simulaties waarin één simulatie ontbreekt:


32.8. Het overzicht van onverwachte fouten
We noemen hier een onverwachte fout een fout die bij normaal gebruik van de webapplicatie niet had mogen optreden. Bijvoorbeeld het aanvragen van een belastingberekening zonder te zijn ingelogd. Niets weerhoudt een gebruiker ervan om URL [/calcul-impot] rechtstreeks in zijn browser in te voeren. Bovendien kan hij, zoals we hebben gezien, een POST uitvoeren op de URL [/calcul-impot] door de verwachte parameters niet mee te sturen. We hebben gezien dat onze webapplicatie correct op dit verzoek kon reageren. We noemen een fout die niet zou mogen optreden binnen de context van de applicatie HTML een ‘onverwachte fout’. Als deze zich voordoet, is dat waarschijnlijk omdat iemand probeert de applicatie te ‘hacken’. Om didactische redenen hebben we besloten om in dergelijke gevallen een foutpagina weer te geven. In de praktijk zouden we de laatste pagina die naar de klant is verzonden opnieuw kunnen weergeven. Hiervoor volstaat het om het laatste verzonden antwoord HTML in de sessie op te slaan. Bij een onverwachte fout sturen we dit antwoord terug. Zo krijgt de gebruiker de indruk dat de server niet op zijn fouten reageert, aangezien de weergegeven pagina niet verandert.
32.8.1. Overzicht van de weergave

De weergave waarin onverwachte fouten worden getoond, ziet er als volgt uit:

De weergave die door de code [vue-erreurs.html] wordt gegenereerd, bestaat uit drie delen:
- 1: de bovenste balk wordt gegenereerd door het fragment [v-bandeau.html] dat al is getoond;
- 2: de onverwachte fout(en);
- 3: een menu met drie links, gegenereerd door het reeds gepresenteerde fragment [v-menu.html];
De weergave van de onverwachte fouten wordt gegenereerd door het volgende script [vue-erreurs.html]:
<!-- document HTML -->
<!doctype html>
<html lang="fr">
<head>
<!-- Vereiste metatags -->
<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">
<!-- banner met 12 kolommen -->
{% include "fragments/v-bandeau.html" %}
<!-- regel met twee secties -->
<div class="row">
<!-- menu met 3 kolommen-->
<div class="col-md-3">
{% include "fragments/v-menu.html" %}
</div>
<!-- foutenlijst met 9 kolommen -->
<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>
Opmerkingen
- regel 16: opname van de banner van de applicatie [1];
- regel 21: opname van het menu [3]. Dit wordt in drie kolommen onder de banner weergegeven;
- regels 24-29: weergave van het foutvenster in negen kolommen;
- regel 25: deze weergave vindt plaats in een Bootstrap-kader met een roze achtergrond;
- regel 26: een inleidende tekst;
- regel 27: de tag <ul> omkadert een lijst met opsommingstekens. Deze lijst met opsommingstekens wordt geleverd door het sjabloon [modèle.erreurs];
We hebben de twee fragmenten van deze weergave al toegelicht:
32.8.2. Visuele test
We maken een testscript voor de weergave [vue-erreurs.html]:

from flask import Flask, render_template, make_response
# Flask-applicatie
app = Flask(__name__, template_folder="../templates", static_folder="../static")
# Home URL
@app.route('/')
def index():
# de gegevens van de pagina worden in een sjabloon ingekapseld
modèle = {}
# we stellen een lijst HTML met fouten samen
content = ""
for erreur in ["erreur1", "erreur2"]:
content += f"<li>{erreur}</li>"
modèle["erreurs"] = content
# menuopties
modèle["optionsMenu"] = [
{"text": "Calcul de l'impôt", "url": '/calculer-impot'},
{"text": 'Liste des simulations', "url": '/lister-simulations'},
{"text": 'Fin de session', "url": '/fin-session'}]
# de pagina weergeven
return make_response(render_template("views/vue-erreurs.html", modèle=modèle))
# hoofdpagina
if __name__ == '__main__':
app.config.update(ENV="development", DEBUG=True)
app.run()
Opmerkingen
- regels 11-15: samenstelling van de foutenlijst HTML;
- regels 17-20: de tabel met menuopties;
Laten we dit script uitvoeren. We krijgen het volgende resultaat:
We werken aan deze weergave totdat we tevreden zijn met het visuele resultaat. Vervolgens kunnen we de weergave integreren in de webapplicatie die we aan het schrijven zijn.

32.8.3. Berekening van het model van de weergave

Zodra het visuele aspect van de weergave is vastgesteld, kunnen we overgaan tot de berekening van het model van de weergave onder reële omstandigheden. Laten we nog eens kijken naar de statuscodes die naar deze weergave leiden. Deze zijn te vinden in het configuratiebestand:
# de weergaven HTML en hun sjablonen zijn afhankelijk van de status die door de controller wordt geretourneerd
"views": [
{
# authenticatieweergave
"états": [
# /init-session geslaagd
700,
# /sessie-beëindigen
400,
# /gebruiker-authenticeren mislukt
201
],
"view_name": "views/vue-authentification.html",
"model_for_view": ModelForAuthentificationView()
},
{
# overzicht belastingberekening
"états": [
# /gebruikersauthenticatie geslaagd
200,
# /belasting-berekenen geslaagd
300,
# /belasting-berekenen mislukt
301,
# /belastingberekening-weergeven
800
],
"view_name": "views/vue-calcul-impot.html",
"model_for_view": ModelForCalculImpotView()
},
{
# overzicht van de lijst met simulaties
"états": [
# /simulaties-weergeven
500,
# /simulatie-verwijderen
600
],
"view_name": "views/vue-liste-simulations.html",
"model_for_view": ModelForListeSimulationsView()
}
],
# overzicht van onverwachte fouten
"view-erreurs": {
"view_name": "views/vue-erreurs.html",
"model_for_view": ModelForErreursView()
},
Het zijn de statuscodes die niet naar een weergave leiden (HTML in de regels 3-41) die ervoor zorgen dat de weergave van onverwachte fouten wordt getoond.
De berekening van het model van de weergave [vue-erreurs.html] wordt uitgevoerd door de volgende klasse [ModelForErreursView]:
from flask import Request
from werkzeug.local import LocalProxy
from InterfaceModelForView import InterfaceModelForView
class ModelForErreursView(InterfaceModelForView):
def get_model_for_view(self, request: Request, session: LocalProxy, config: dict, résultat: dict) -> dict:
# het model
modèle = {}
# de fouten
modèle["erreurs"] = ""
for erreur in résultat['réponse']:
modèle['erreurs'] += f"<li>{erreur}</li>"
# menu
modèle["optionsMenu"] = [
{"text": "Calcul de l'impôt", "url": '/afficher-calcul-impot'},
{"text": 'Liste des simulations', "url": '/lister-simulations'},
{"text": 'Fin de session', "url": '/fin-session'}]
# het model wordt weergegeven
return modèle
Opmerkingen
- regels 11-14: berekening van het model [modèle.erreurs] dat wordt gebruikt door de weergave [vue-erreurs.html];
- regels 16-197: berekening van het sjabloon [modèle.optionsMenu] dat wordt gebruikt door het fragment [v-menu.html];
32.8.4. Tests [Postman]
We voeren het volgende uit:
- de actie [/init-session/html];
- vervolgens de actie [/init-session/x];
Het antwoord HTML is dan als volgt:

32.9. Implementatie van de acties in het menu van de applicatie
Hier gaan we in op de implementatie van de menuacties. Laten we nog even de betekenis van de links die we zijn tegengekomen op een rijtje zetten
Weergave | Link | Doel | Functie |
Belastingberekening | [Liste des simulations] | [/lister-simulations] | De lijst met simulaties opvragen |
[Fin de session] | |||
Lijst met simulaties | [Calcul de l’impôt] | [/afficher-calcul-impot] | Weergave van de belastingberekening |
[Fin de session] | |||
Onverwachte fouten | [Calcul de l’impôt] | [/afficher-calcul-impot] | Weergave van de belastingberekening |
[Liste des simulations] | |||
[Fin de session] |
Houd er rekening mee dat een klik op een link een GET naar de bestemming van de link veroorzaakt. De acties [/lister-simulations, /fin-session] zijn geïmplementeerd met een bewerking GET, waardoor we ze als bestemmingen van links kunnen instellen. Wanneer de actie via een POST wordt uitgevoerd, is het gebruik van een link niet meer mogelijk, tenzij deze wordt gekoppeld aan JavaScript.
32.9.1. De actie [/afficher-calcul-impot]
Uit de bovenstaande acties blijkt dat de actie [/afficher-calcul-impot] nog niet is geïmplementeerd. Dit is een navigatiehandeling tussen twee weergaven: de server jSON of XML heeft geen reden om deze te implementeren, omdat zij het begrip ‘weergave’ niet kennen. Het is de server HTML die dit begrip introduceert.
We moeten dus de actie [/afficher-calcul-impot] implementeren. Dit stelt ons in staat om de werkwijze voor het implementeren van een actie binnen de server te herzien.
Allereerst moeten we een nieuwe secundaire controller toevoegen. We noemen deze [AfficherCalculImpotController]:

Deze controller moet worden toegevoegd aan het configuratiebestand [config]:
# de controllers
from AfficherCalculImpotController import AfficherCalculImpotController
from AuthentifierUtilisateurController import AuthentifierUtilisateurController
from CalculerImpotController import CalculerImpotController
from CalculerImpotsController import CalculerImpotsController
from FinSessionController import FinSessionController
from GetAdminDataController import GetAdminDataController
…
# toegestane acties en hun controllers
"controllers": {
# initialisatie van een reken-sessie
"init-session": InitSessionController(),
# authenticatie van een gebruiker
"authentifier-utilisateur": AuthentifierUtilisateurController(),
# berekening van de belasting in de individuele modus
"calculer-impot": CalculerImpotController(),
# berekening van de belasting in batchmodus
"calculer-impots": CalculerImpotsController(),
# lijst met simulaties
"lister-simulations": ListerSimulationsController(),
# verwijdering van een simulatie
"supprimer-simulation": SupprimerSimulationController(),
# beëindiging van de berekeningssessie
"fin-session": FinSessionController(),
# weergave van het overzicht van de belastingberekening
"afficher-calcul-impot": AfficherCalculImpotController(),
# opvragen van gegevens bij de belastingdienst
"get-admindata": GetAdminDataController(),
# hoofdcontroller
"main-controller": MainController()
},
…
# de weergaven HTML en hun sjablonen zijn afhankelijk van de status die door de controller wordt geretourneerd
"views": [
{
# authenticatiepagina
…
},
{
# pagina voor belastingberekening
"états": [
# /gebruiker-authenticeren geslaagd
200,
# /belasting-berekenen geslaagd
300,
# /belasting-berekenen mislukt
301,
# /belastingberekening-weergeven
800
],
"view_name": "views/vue-calcul-impot.html",
"model_for_view": ModelForCalculImpotView()
},
{…
}
],
- regel 2: de nieuwe controller;
- regel 28: de nieuwe actie en de bijbehorende controller;
- regel 51: de nieuwe controller zal de statuscode 800 retourneren. Bij het wisselen van weergave kan er geen fout optreden. De weergegeven weergave is de weergave [vue-calcul-impot.html] die we hebben bestudeerd, uitgelegd en getest;
De controller [AfficherCalculImpotController] ziet er als volgt uit:
from flask_api import status
from werkzeug.local import LocalProxy
from InterfaceController import InterfaceController
class AfficherCalculImpotController(InterfaceController):
def execute(self, request: LocalProxy, session: LocalProxy, config: dict) -> (dict, int):
# de elementen van het pad worden opgehaald
dummy, action = request.path.split('/')
# weergave wijzigen – alleen een statuscode instellen
return {"action": action, "état": 800, "réponse": ""}, status.HTTP_200_OK
Opmerkingen
- regel 6: net als de andere secundaire controllers implementeert de nieuwe controller de interface [InterfaceController];
- regel 13: het wisselen van weergave is eenvoudig te implementeren: het volstaat om een statuscode te retourneren die gekoppeld is aan de doelweergave, in dit geval code 800 zoals hierboven besproken;
32.9.2. De actie [/fin-session]
De actie [/fin-session] is bijzonder. Deze leidt niet direct naar een weergave, maar naar een omleiding. Ter herinnering: de omleidingen worden in de configuratie [config] als volgt ingesteld:
# omleidingen
"redirections": [
{
"états": [
400, # /sessie succesvol beëindigd
],
# omleiding naar
"to": "/init-session/html",
}
],
Er is slechts één omleiding in de applicatie:
- wanneer de controller de statuscode [400] retourneert (regel 5), moet de client worden omgeleid naar URL [http://machine:port/chemin/init-session/html] (regel 8);
De statuscode [400] wordt geretourneerd na een succesvolle actie [/fin-session]. Waarom moet de client dan worden omgeleid naar URL en [/init-session/html]? Omdat de actie [/fin-session] het sessietype uit de websessie verwijdert. Daardoor is niet meer bekend dat er sprake is van een HTML-sessie. De klant moet dus worden omgeleid. Dit gebeurt met behulp van de actie [/init-session/html].
De omleidingen HTML worden beheerd door de klasse [HtmlResponse]:
def build_http_response(self, request: LocalProxy, session: LocalProxy, config: dict, status_code: int,
résultat: dict) -> (Response, int):
# het antwoord HTML is afhankelijk van de statuscode die door de controller wordt geretourneerd
état = résultat["état"]
# moet er een omleiding plaatsvinden?
for redirection in config["redirections"]:
# statussen waarbij een omleiding nodig is
états = redirection["états"]
if état in états:
# er moet een omleiding worden uitgevoerd
return redirect(f"{redirection['to']}"), status.HTTP_302_FOUND
# aan elke status hoort een weergave
# deze wordt gezocht in de lijst met weergaven
..
- de regels 6-12 behandelen de omleidingen;
- regel 7: config[‘redirections’] is een lijst met omleidingen. Elke omleiding is een woordenboek met de volgende sleutels:
- [états]: de door de controller geretourneerde statussen die tot een omleiding leiden;
- [to]: het omleidingsadres;
- regels 7-12: de lijst met omleidingen wordt doorlopen;
- regel 9: voor elke omleiding worden de statussen opgehaald die ernaartoe leiden;
- regel 10: als de geteste status in deze lijst staat, wordt de omleiding uitgevoerd, regel 12;
- regel 12: ter herinnering: de methode [build_http_response] moet een tuple met twee elementen retourneren:
- [response]: het te genereren antwoord HTTP. Dit wordt samengesteld met de functie [redirect], waarvan de parameter het omleidingsadres is;
- [status_code]: de statuscode van het antwoord HTTP, in dit geval de code [status.HTTP_302_FOUND] die de client aangeeft dat hij moet worden omgeleid;
Laten we een test uitvoeren met [Postman]. We:
- een sessie initialiseren: HTML [init-session/html];
- verifiëren we ons: [/authentifier-utilisateur];
- beëindigen we de sessie [/fin-session];

Het antwoord van de server is als volgt:

We hebben de authenticatiepagina ontvangen. Dit is inderdaad de pagina die we verwachtten. Laten we nu eens kijken hoe deze is verkregen. Laten we naar de console gaan [Postman] (Ctrl-Alt-C):

- in [1], de actie [/fin-session];
- naar [2-3]; de statuscode 302 van HTTP die door de server wordt geretourneerd, geeft de client aan dat hij wordt omgeleid;
- in [4] volgt de client [Postman] de omleiding;
32.10. Testen van de applicatie HTML onder reële omstandigheden
De code is geschreven en elke actie is getest met [Postman]. Nu moeten we de opeenvolging van de weergaven nog in een echte situatie testen. We hebben een manier nodig om de sessie HTML te initialiseren. We weten dat we het verzoek [/init-session/html] naar de server moeten sturen. Dat is niet erg handig. We zouden liever beginnen met URL en [/].
We hebben in het hoofdscript [main] de volgende route geschreven:
from flask import request, Flask, session, url_for, redirect
…
…
@app.route('/', methods=['GET'])
def index() -> tuple:
# omleiding naar /init-session/html
return redirect(url_for("init_session", type_response="html"), status.HTTP_302_FOUND)
…
# init-session
@app.route('/init-session/<string:type_response>', methods=['GET'])
def init_session(type_response: str) -> tuple:
# de controller die aan de actie is gekoppeld, wordt uitgevoerd
return front_controller()
- regels 4-7: beheer van de route [/]. Het startpunt van de webapplicatie is URL[/init-session/html] (regel 10). Ook op regel 7 leiden we de client om naar deze URL:
- de functie [url_for] wordt geïmporteerd in regel 1. Deze heeft hier twee parameters (regel 7):
- de eerste parameter is de naam van een van de routeringsfuncties, in dit geval die van regel 11. We zien dat deze functie een parameter [type_response] verwacht, wat het type (json, xml, html) is van het door de klant gewenste antwoord;
- de tweede parameter neemt de naam van de parameter uit regel 11 over, [type_response], en kent er een waarde aan toe. Als er nog andere parameters zouden zijn, zouden we deze handeling voor elk van hen herhalen;
- hiermee wordt de URL gekoppeld aan de functie die wordt aangeduid door de twee parameters die eraan zijn toegekend. In dit geval levert dit de URL uit regel 10 op, waarbij de parameter wordt vervangen door de waarde [/init-session/html];
- de functie [redirect] is geïmporteerd in regel 1. Deze heeft als taak een HTTP-omleidingsheader naar de client te sturen:
- de eerste parameter is de URL waarnaar de klant moet worden omgeleid;
- de tweede parameter is de statuscode van het antwoord HTTP dat aan de klant wordt gegeven. De code [status.HTTP_302_FOUND] komt overeen met een omleiding HTTP;
We zijn er klaar voor. We laten nu enkele reeksen van weergaven zien.
In onze browser schakelen we het bijhouden van verzoeken in (F12 in Chrome, Firefox, Edge) en vragen we de start-URL [http://localhost:5000/] op. Het antwoord van de server is als volgt:

Als we kijken naar de netwerkverkeer dat plaatsvond tussen de client en de server:

- zien we dat de browser in [4, 5] een verzoek heeft ontvangen om door te verwijzen naar URL [/init-session/html];
Laten we het formulier invullen dat we hebben ontvangen;

Laten we vervolgens enkele simulaties uitvoeren:


Laten we de lijst met simulaties opvragen:

Laten we de eerste simulatie verwijderen:

Laten we de sessie beëindigen:

De lezer wordt uitgenodigd om nog meer tests uit te voeren.