35. Toepassingsopdracht: versie 15
35.1. Inleiding
Deze versie is bedoeld om problemen op te lossen die verband houden met het verversen van de applicatiepagina’s in de browser (F5). Laten we een voorbeeld nemen. De gebruiker heeft zojuist de simulatie met id=3 verwijderd:

Na het verwijderen is URL in de browser [/supprimer-simulation/3/…]. Als de gebruiker de pagina vernieuwt (F5), wordt de URL [1] opnieuw afgespeeld. Er wordt dus opnieuw gevraagd om de simulatie met id=3 te verwijderen. Het resultaat is dan als volgt:

Hier volgt nog een voorbeeld. De gebruiker heeft zojuist een belasting berekend:
- in [1] wordt de URL [/calculer-impot], die zojuist is opgevraagd met een POST, opnieuw uitgevoerd;
Als de gebruiker de pagina vernieuwt (F5), krijgt hij een waarschuwingsbericht te zien:

Bij het verversen van de pagina wordt het laatste verzoek dat door de browser is uitgevoerd opnieuw afgespeeld, in dit geval een [POST /calculer-impot]. Wanneer gevraagd wordt om een POST opnieuw uit te voeren, geven browsers een waarschuwing weer die vergelijkbaar is met de bovenstaande. Deze waarschuwt dat er een reeds uitgevoerde actie opnieuw wordt uitgevoerd. Stel dat deze POST een aankoop heeft gedaan, dan zou het jammer zijn om deze opnieuw uit te voeren.
Bovendien gaan we de mogelijkheid voor de gebruiker beperken om URL in zijn browser in te voeren. Laten we bijvoorbeeld een van de vorige weergaven nemen:

De links op deze pagina zijn:
- [Liste des simulations], gekoppeld aan URL en [/lister-simulations];
- [Fin de session] gekoppeld aan URL en [/fin-session];
- [Valider] gekoppeld aan URL (hierboven niet weergegeven) [/calculer-impot];
Zodra het scherm met de belastingberekening wordt weergegeven, accepteren we alleen de acties [/lister-simulations, /fin-session, /calculer-impot]. Als de gebruiker een andere actie in zijn browser invoert, wordt er een foutmelding weergegeven. We voeren dit soort controles uit voor alle vier de schermen van de applicatie.
We stellen voor om het probleem van het verversen van de pagina’s als volgt op te lossen:
- we maken onderscheid tussen twee soorten acties:
- de acties ADS (Action Do Something) die de status van de applicatie wijzigen. Deze acties hebben doorgaans parameters in de URL of in de body van het verzoek;
- de acties ASV (Action Show View) die een weergave weergeven zonder de status van de applicatie te wijzigen. Er zullen evenveel acties ASV zijn als er weergaven V zijn. De acties ASV hebben geen parameters;
- de acties ADS werden tot nu toe uitgevoerd en eindigden met het weergeven van een weergave V nadat het bijbehorende model M was voorbereid. Voortaan zullen ze het model M van de weergave in de sessie plaatsen en de browser vragen om door te verwijzen naar de actie ASV, die verantwoordelijk is voor het weergeven van de weergave V;
- de V-weergaven worden pas weergegeven na voltooiing van een actie ASV. Ze halen hun sjabloon uit de sessie;
Het voordeel van deze methode is dat de browser de acties URL en ASV in de adresbalk weergeeft. Bij het verversen van de pagina wordt de actie ASV opnieuw uitgevoerd. Deze actie wijzigt de status van de applicatie niet en maakt gebruik van een sessiesjabloon. Dezelfde pagina wordt dus opnieuw weergegeven zonder ongewenste bijwerkingen. Uiteindelijk zal de gebruiker, vanwege de omleidingen, in zijn browser alleen acties URL en ASV zien en de indruk hebben dat hij van pagina naar pagina navigeert;
35.2. Implementatie

De map [impots/http-servers/10] wordt in eerste instantie verkregen door de map [impots/http-servers/09] te kopiëren. Vervolgens wordt deze gewijzigd.
35.2.1. De nieuwe routes

In de twee bestanden [routes_with_csrftoken] en [routes_without_csrftoken] moeten we de vier routes aanmaken voor de vier acties ASV die de vier weergaven tonen. De overige routes blijven ongewijzigd.
In [routes_with_csrftoken]:
# belastingberekening weergeven
@app.route('/afficher-vue-calcul-impot/<string:csrf_token>', methods=['GET'])
def afficher_vue_calcul_impot(csrf_token: str) -> tuple:
# de bij de actie behorende controller wordt uitgevoerd
return front_controller()
# weergave-authenticatie-view
@app.route('/afficher-vue-authentification/<string:csrf_token>', methods=['GET'])
def afficher_vue_authentification(csrf_token: str) -> tuple:
# de bij de actie behorende controller wordt uitgevoerd
return front_controller()
# weergave-overzicht-simulaties
@app.route('/afficher-vue-liste-simulations/<string:csrf_token>', methods=['GET'])
def afficher_vue_liste_simulations(csrf_token: str) -> tuple:
# de bij de actie behorende controller wordt uitgevoerd
return front_controller()
# lijstweergave-fouten weergeven
@app.route('/afficher-vue-liste-erreurs/<string:csrf_token>', methods=['GET'])
def afficher_vue_liste_erreurs(csrf_token: str) -> tuple:
# de bij de actie behorende controller wordt uitgevoerd
return front_controller()
In de regels 1-23 hebben we vier routes aangemaakt voor vier acties ASV:
- [/afficher-vue-authentification], regel 8, toont het authenticatiescherm;
- [/afficher-vue-calcul-impot], regel 2, toont het scherm voor de belastingberekening;
- [/afficher-vue-liste-simulations], regel 14, toont het simulatieoverzicht;
- [/afficher-vue-liste-erreurs], regel 20, toont het overzicht van onverwachte fouten;
Hetzelfde doen we in het bestand [routes_with_csrftoken] voor wegen zonder tol:
# routes ASV -------------------------
# belastingberekening-weergave
@app.route('/afficher-vue-calcul-impot', methods=['GET'])
def afficher_vue_calcul_impot() -> tuple:
# de bij de actie behorende controller wordt uitgevoerd
return front_controller()
# weergave-authenticatie
@app.route('/afficher-vue-authentification', methods=['GET'])
def afficher_vue_authentification() -> tuple:
# de bij de actie behorende controller wordt uitgevoerd
return front_controller()
# weergave-overzicht-simulaties
@app.route('/afficher-vue-liste-simulations', methods=['GET'])
def afficher_vue_liste_simulations() -> tuple:
# de bij de actie behorende controller wordt uitgevoerd
return front_controller()
# lijstweergave-fouten weergeven
@app.route('/afficher-vue-liste-erreurs', methods=['GET'])
def afficher_vue_liste_erreurs() -> tuple:
# de aan de actie gekoppelde controller wordt uitgevoerd
return front_controller()
35.2.2. De nieuwe verkeersleiders

De controller [AfficherVueAuthentificationController] voert de actie ASV [/afficher-vue-authentification] uit:
from flask_api import status
from werkzeug.local import LocalProxy
from InterfaceController import InterfaceController
class AfficherVueAuthentificationController(InterfaceController):
def execute(self, request: LocalProxy, session: LocalProxy, config: dict) -> (dict, int):
# de elementen van het pad worden opgehaald
params = request.path.split('/')
action = params[1]
# van weergave wisselen – alleen een statuscode instellen
return {"action": action, "état": 1100, "réponse": ""}, status.HTTP_200_OK
Er is gezegd dat de acties ASV geen parameters hebben en de status van de applicatie niet wijzigen. We geven alleen de gewenste weergave weer door in regel 14 een statuscode in te stellen.
De controller [AfficherVueCalculImpotController] voert de actie ASV uit [/afficher-vue-calcul-impot]:
from flask_api import status
from werkzeug.local import LocalProxy
from InterfaceController import InterfaceController
class AfficherVueCalculImpotController(InterfaceController):
def execute(self, request: LocalProxy, session: LocalProxy, config: dict) -> (dict, int):
# de elementen van het pad worden opgehaald
params = request.path.split('/')
action = params[1]
# weergavewijziging – alleen een statuscode instellen
return {"action": action, "état": 1400, "réponse": ""}, status.HTTP_200_OK
De controller [AfficherVueListeSimulationsController] voert de actie ASV [/afficher-vue-liste-simulations] uit:
from flask_api import status
from werkzeug.local import LocalProxy
from InterfaceController import InterfaceController
class AfficherVueListeSimulationsController(InterfaceController):
def execute(self, request: LocalProxy, session: LocalProxy, config: dict) -> (dict, int):
# de elementen van het pad worden opgehaald
params = request.path.split('/')
action = params[1]
# weergavewijziging – alleen een statuscode instellen
return {"action": action, "état": 1200, "réponse": ""}, status.HTTP_200_OK
De controller [AfficherVueListeErreursController] voert de actie ASV uit [/afficher-vue-liste-erreurs]:
from flask_api import status
from werkzeug.local import LocalProxy
from InterfaceController import InterfaceController
class AfficherVueListeErreursController(InterfaceController):
def execute(self, request: LocalProxy, session: LocalProxy, config: dict) -> (dict, int):
# de elementen van het pad worden opgehaald
params = request.path.split('/')
action = params[1]
# weergavewijziging – alleen een statuscode instellen
return {"action": action, "état": 1300, "réponse": ""}, status.HTTP_200_OK
Laten we de statuscodes samenvatten:
- code 1100 moet het authenticatiescherm weergeven;
- code 1400 moet het scherm voor de belastingberekening weergeven;
- code 1200 moet het simulatiescherm weergeven;
- code 1300 moet het scherm met onverwachte fouten weergeven;
35.2.3. De nieuwe configuratie MVC

Omdat de configuratie MVC te omvangrijk werd, is deze opgesplitst in vier bestanden:
- [controllers]: de lijst met C-controllers van de applicatie MVC;
- [ads_actions]: bevat de lijst met acties uit ADS (Action Do Something);
- [asv_actions]: lijst met acties van ASV (Action Show View);
- [responses]: geeft een overzicht van de antwoordklassen HTTP van de applicatie;
Laten we beginnen met het eenvoudigste, het antwoordbestand HTTP [responses]:
def configure(config: dict) -> dict:
# configuratie van de applicatie MVC
# de antwoorden HTTP
from HtmlResponse import HtmlResponse
from JsonResponse import JsonResponse
from XmlResponse import XmlResponse
# de verschillende soorten antwoorden (json, xml, html)
responses = {
"json": JsonResponse(),
"html": HtmlResponse(),
"xml": XmlResponse()
}
# we genereren het antwoordwoordenboek HTTP
return {
# antwoorden HTTP
"responses": responses,
}
Geen verrassing.
Het bestand [controllers] met de controllers bevat eveneens geen verrassingen. Er zijn simpelweg de nieuwe controllers van de acties uit ASV aan toegevoegd.
def configure(config: dict) -> dict:
# configuratie van de applicatie MVC
# de hoofdcontroller
from MainController import MainController
# de actiecontrollers ADS
from AuthentifierUtilisateurController import AuthentifierUtilisateurController
from CalculerImpotController import CalculerImpotController
from CalculerImpotsController import CalculerImpotsController
from FinSessionController import FinSessionController
from GetAdminDataController import GetAdminDataController
from InitSessionController import InitSessionController
from ListerSimulationsController import ListerSimulationsController
from SupprimerSimulationController import SupprimerSimulationController
from AfficherCalculImpotController import AfficherCalculImpotController
# de actiecontrollers ASV
from AfficherVueCalculImpotController import AfficherVueCalculImpotController
from AfficherVueAuthentificationController import AfficherVueAuthentificationController
from AfficherVueListeErreursController import AfficherVueListeErreursController
from AfficherVueListeSimulationsController import AfficherVueListeSimulationsController
# toegestane acties en de bijbehorende controleurs
controllers = {
# initialisatie van een reken-sessie
"init-session": InitSessionController(),
# authenticatie van een gebruiker
"authentifier-utilisateur": AuthentifierUtilisateurController(),
# link naar belastingberekeningsweergave
"afficher-calcul-impot": AfficherCalculImpotController(),
# belastingberekening in individuele modus
"calculer-impot": CalculerImpotController(),
# belastingberekening in batchmodus
"calculer-impots": CalculerImpotsController(),
# lijst met simulaties
"lister-simulations": ListerSimulationsController(),
# een simulatie verwijderen
"supprimer-simulation": SupprimerSimulationController(),
# beëindiging van de berekeningssessie
"fin-session": FinSessionController(),
# gegevens opvragen bij de belastingdienst
"get-admindata": GetAdminDataController(),
# hoofdcontroller
"main-controller": MainController(),
# weergave van het authenticatiescherm
"afficher-vue-authentification": AfficherVueAuthentificationController(),
# weergave van het scherm voor belastingberekening
"afficher-vue-calcul-impot": AfficherVueCalculImpotController(),
# weergave van het simulatiescherm
"afficher-vue-liste-simulations": AfficherVueListeSimulationsController(),
# weergave van het foutenoverzicht
"afficher-vue-liste-erreurs": AfficherVueListeErreursController()
}
# de configuratie van de controleurs wordt weergegeven
return {
# controllers
"controllers": controllers,
}
De configuratie [asv_actions] van de acties ASV is als volgt:
def configure(config: dict) -> dict:
# configuratie van de applicatie MVC
# de weergaven HTML en hun sjablonen zijn afhankelijk van de status die door de controller wordt geretourneerd
# acties ASV (actie ‘Weergave tonen’)
asv = [
{
# authenticatieweergave
"états": [
1100, # /authenticatieweergave-tonen
],
"view_name": "views/vue-authentification.html",
},
{
# weergave van de belastingberekening
"états": [
1400, # /belastingberekening-weergave-tonen
],
"view_name": "views/vue-calcul-impot.html",
},
{
# overzicht van de lijst met simulaties
"états": [
1200, # /weergave-lijst-simulaties
],
"view_name": "views/vue-liste-simulations.html",
},
{
# weergave van de foutenlijst
"états": [
1300, # /weergave-foutenlijst
],
"view_name": "views/vue-erreurs.html",
},
]
# de configuratie wordt weergegeven ASV
return {
# weergaven en sjablonen
"asv": asv,
}
- Het bestand [asv_actions] bevat de vier nieuwe acties, waarvan we de werking nog even in herinnering brengen:
- ze hebben geen parameters;
- ze geven een specifieke weergave weer waarvan het sjabloon in de sessie aanwezig is;
- de lijst [asv] in de regels 6-35 koppelt een weergave aan elke actie ASV;
Het bestand [ads_actions] bevat de acties ADS:
def configure(config: dict) -> dict:
# configuratie van de applicatie MVC
# de sjablonen van de weergaven
from ModelForAuthentificationView import ModelForAuthentificationView
from ModelForCalculImpotView import ModelForCalculImpotView
from ModelForErreursView import ModelForErreursView
from ModelForListeSimulationsView import ModelForListeSimulationsView
# acties ADS (Action Do Something)
ads = [
{
"états": [
400, # /sessie succesvol beëindigd
],
# omleiding naar actie ADS
"to": "/init-session/html",
},
{
"états": [
700, # /sessie-starten - geslaagd
201, # /gebruiker-authenticeren mislukt
],
# omleiding naar actie ASV
"to": "/afficher-vue-authentification",
# sjabloon van de volgende weergave
"model_for_view": ModelForAuthentificationView()
},
{
"états": [
200, # /gebruiker-authenticeren geslaagd
300, # /belasting-berekenen geslaagd
301, # /belasting-berekenen mislukt
800, # /belastingberekening-weergeven link
],
# doorverwijzing naar actie ASV
"to": "/afficher-vue-calcul-impot",
# sjabloon van de volgende weergave
"model_for_view": ModelForCalculImpotView()
},
{
"états": [
500, # /simulaties-weergeven geslaagd
600, # /simulatie-verwijderen geslaagd
],
# omleiding naar actie ASV
"to": "/afficher-vue-liste-simulations",
# sjabloon van de volgende weergave
"model_for_view": ModelForListeSimulationsView()
},
]
# weergave van onverwachte fouten
view_erreurs = {
# omleiding naar actie ASV
"to": "/afficher-vue-liste-erreurs",
# sjabloon van de volgende weergave
"model_for_view": ModelForErreursView()
}
# we geven de configuratie weer MVC
return {
# acties ADS
"ads": ads,
# weergave van onverwachte fouten
"view_erreurs": view_erreurs,
}
- regels 11-51: de lijst met acties ADS (Action Do Something). Alle acties uit eerdere versies zijn aanwezig. De werking ervan is echter veranderd:
- ze geven geen weergave V weer. Ze bereiden alleen het model M van deze weergave V voor;
- ze vragen om de weergave V weer te geven via een omleiding naar de actie ASV die aan de weergave V is gekoppeld;
- de acties ADS leiden niet allemaal tot een omleiding naar een actie ASV: regels 12-18: de actie ADS [/fin-session] leidt tot een omleiding naar de actie ADS [/init-session/html]. Om de omleidingen ADS -> ADS en ADS -> ASV van elkaar te onderscheiden, kan het sjabloon [model_for_view] worden gebruikt. Dit bestaat niet voor de omleidingen ADS -> ADS;
Het bestand [main/config], waarin alle configuraties zijn samengebracht, verandert als volgt:
def configure(config: dict) -> dict:
# syspath-configuratie
import syspath
config['syspath'] = syspath.configure(config)
# instellingen van de applicatie
import parameters
config['parameters'] = parameters.configure(config)
# configuratie van de database
import database
config["database"] = database.configure(config)
# instantiëren van de applicatielagen
import layers
config['layers'] = layers.configure(config)
# configuratie van de laag MVC
config['mvc'] = {}
# configuratie van de controllers van de laag [web]
import controllers
config['mvc'].update(controllers.configure(config))
# acties ASV (Action Show View)
import asv_actions
config['mvc'].update(asv_actions.configure(config))
# acties ADS (actie 'Do Something')
import ads_actions
config['mvc'].update(ads_actions.configure(config))
# configuratie van de antwoorden HTTP
import responses
config['mvc'].update(responses.configure(config))
# de configuratie wordt doorgegeven
return config
35.2.4. De nieuwe sjablonen

De sjablonen zullen nieuwe informatie genereren. Laten we bijvoorbeeld het sjabloon voor het authenticatiescherm nemen:
from flask import Request
from werkzeug.local import LocalProxy
from AbstractBaseModelForView import AbstractBaseModelForView
class ModelForAuthentificationView(AbstractBaseModelForView):
def get_model_for_view(self, request: Request, session: LocalProxy, config: dict, résultat: dict) -> dict:
# de gegevens van de pagina in een sjabloon inkapselen
modèle = {}
…
# CSRF-token
modèle['csrf_token'] = super().get_csrftoken(config)
# mogelijke acties vanuit de weergave
modèle['actions_possibles'] = ["afficher-vue-authentification", "authentifier-utilisateur"]
# het sjabloon wordt weergegeven
return modèle
- De nieuwigheid staat op regel 17: elk model genereert een lijst met mogelijke acties wanneer de weergave V, waarvan het model M is, wordt weergegeven. Om deze acties te bekijken, moet je teruggaan naar de weergave V. In het geval van de authenticatieweergave:

- zien we dat de authenticatieweergave slechts één actie biedt, namelijk die van de knop [Valider]. Deze actie is [/authentifier-utilisateur];
We hebben geschreven:
# mogelijke acties vanuit de weergave
modèle['actions_possibles'] = ["afficher-vue-authentification", "authentifier-utilisateur"]
Op het bovenstaande scherm is te zien dat als de gebruiker het scherm vernieuwt, de actie [1] [/afficher-vue-authentification] opnieuw wordt uitgevoerd. Deze actie moet dus worden toegestaan. We hadden deze ook kunnen weigeren, maar dan zou de gebruiker elke keer dat hij de pagina opnieuw laadt een foutmelding krijgen. We vonden dit niet wenselijk.
De mogelijke acties worden in het sjabloon van de weergave geplaatst. We weten dat dit sjabloon in de sessie wordt opgeslagen.
Dit doen we voor elk van de vier weergaven. De mogelijke acties zijn dan als volgt:
Authenticatieweergave
modèle['actions_possibles'] = ["afficher-vue-authentification", "authentifier-utilisateur"]
Weergave voor de belastingberekening
# mogelijke acties vanuit de weergave
modèle['actions_possibles'] = ["afficher-vue-calcul-impot", "calculer-impot", "lister-simulations","fin-session"]
Overzicht van de simulatielijst
# mogelijke acties vanuit de weergave
actions_possibles = ["afficher-vue-liste-simulations", "afficher-calcul-impot", "fin-session"]
if len(modèle['simulations']) != 0:
actions_possibles.append("supprimer-simulation")
modèle['actions_possibles'] = actions_possibles
Overzicht van de foutenlijst
# mogelijke acties vanuit de weergave
modèle['actions_possibles'] = ["afficher-vue-liste-erreurs", "afficher-calcul-impot", "lister-simulations", "fin-session"]
35.2.5. De nieuwe hoofdcontroller

De controller [MainController] ondergaat enkele wijzigingen:
# afhankelijkheden importeren
import threading
import time
from random import randint
from flask_api import status
from flask_wtf.csrf import generate_csrf, validate_csrf
from werkzeug.local import LocalProxy
from wtforms import ValidationError
from InterfaceController import InterfaceController
from Logger import Logger
from SendAdminMail import SendAdminMail
def send_adminmail(config: dict, message: str):
# er wordt een e-mail verzonden naar de beheerder van de applicatie
config_mail = config['parameters']['adminMail']
config_mail["logger"] = config['logger']
SendAdminMail.send(config_mail, message)
# hoofdapplicatiebeheerder
class MainController(InterfaceController):
def execute(self, request: LocalProxy, session: LocalProxy, config: dict) -> (dict, int):
# het verzoek wordt verwerkt
type_response1 = None
logger = None
try:
# de elementen van het pad worden opgehaald
params = request.path.split('/')
# de actie is het eerste element
action = params[1]
# loggen
logger = Logger(config['parameters']['logsFilename'])
…
# de actie /afficher-vue-liste-erreurs is bijzonder
# er wordt geen controle uitgevoerd, anders bestaat het risico dat er een oneindige lus van omleidingen ontstaat
erreur = False
if action != "afficher-vue-liste-erreurs":
# bij een fout is [resultaat] het resultaat dat naar de klant moet worden verzonden
(erreur, résultat, type_response1) = MainController.check_action(params, session, config)
# als er geen fout is, wordt de actie uitgevoerd
if not erreur:
# de bij de actie behorende controller wordt uitgevoerd
controller = config['mvc']['controllers'][action]
résultat, status_code = controller.execute(request, session, config)
except BaseException as exception:
# (onverwachte) uitzonderingen
résultat = {"action": action, "état": 131, "réponse": [f"{exception}"]}
erreur = True
finally:
pass
# uitvoeringsfout
if erreur:
# ongeldige aanvraag
status_code = status.HTTP_400_BAD_REQUEST
if config['parameters']['with_csrftoken']:
# csrf_token wordt aan het resultaat toegevoegd
résultat['csrf_token'] = generate_csrf()
….
# het antwoord HTTP wordt verzonden
return response, status_code
@staticmethod
def check_action(params: list, session: LocalProxy, config: dict) -> (bool, dict, str):
…
# resultaat van de methode
return erreur, résultat, type_response1
- regels 39-44: de eerste controles worden uitgevoerd op de actie. We komen hier later op terug. De statische methode [MainController.check_action] retourneert een tuple van drie elementen:
- [erreur]: True als er een fout is gedetecteerd, False anders;
- [résultat]: een foutresultaat als (fout == True), None anders;
- [type_response1]: het type (json, xml, html, None) van de sessie, het type dat in de sessie is aangetroffen;
- regel 39: er wordt geen controle uitgevoerd als de actie de actie ASV [afficher-vue-liste-erreurs] is, die de lijst met fouten weergeeft. Als er namelijk tijdens deze actie een fout zou worden gevonden, zou men opnieuw worden doorgestuurd naar de actie [/afficher-vue-liste-erreurs] en zou men in een oneindige lus van omleidingen terechtkomen;
- De statische methode [check_action] is als volgt:
@staticmethod
def check_action(params: list, session: LocalProxy, config: dict) -> (bool, dict, str):
# de lopende actie wordt opgehaald
action = params[1]
# geen fout en geen resultaat bij het begin
erreur = False
résultat = None
# het sessietype moet bekend zijn vóór bepaalde acties ADS
type_response1 = session.get('typeResponse')
if type_response1 is None and action != "init-session":
# er wordt een foutmelding weergegeven
résultat = {"action": action, "état": 101,
"réponse": ["pas de session en cours. Commencer par action [init-session]"]}
erreur = True
# voor bepaalde acties ADS moet men geauthenticeerd zijn
user = session.get('user')
if user is None and action not in ["init-session",
"authentifier-utilisateur",
"afficher-vue-authentification"]:
# er wordt een foutmelding weergegeven
résultat = {"action": action, "état": 101,
"réponse": [f"action [{action}] demandée par utilisateur non authentifié"]}
erreur = True
# vanuit een weergave zijn slechts bepaalde acties mogelijk
if not erreur and action != "init-session":
# momenteel zijn er geen acties mogelijk
actions_possibles = None
# het sjabloon van de toekomstige weergave wordt opgehaald uit de sessie, indien aanwezig
modèle = session.get('modèle')
# als er een sjabloon is gevonden, worden de mogelijke acties ervan opgehaald
if modèle:
actions_possibles = modèle.get('actions_possibles')
# als er een lijst met mogelijke acties is, wordt gecontroleerd of de huidige actie daar deel van uitmaakt
if actions_possibles and action not in actions_possibles:
# de fout wordt genoteerd
résultat = {"action": action, "état": 151,
"réponse": [f"action [{action}] incorrecte dans l'environnement actuel"]}
erreur = True
# fout?
if not erreur and config['parameters']['with_csrftoken']:
# we controleren de geldigheid van het CSRF-token
# csrf_token is het laatste element van het pad
csrf_token = params.pop()
try:
# er wordt een uitzondering gegenereerd als csrf_token ongeldig is
validate_csrf(csrf_token)
except ValidationError as exception:
# ongeldig CSRF-token
résultat = {"action": action, "état": 121, "réponse": [f"{exception}"]}
# de fout wordt genoteerd
erreur = True
# resultaat van de methode
return erreur, résultat, type_response1
- regel 2: de methode [check_action] voert verschillende controles uit op de geldigheid van de lopende actie;
- regels 6-26, 45-57: eerdere versies voerden deze controles al uit;
- regels 28-42: er wordt een nieuwe controle toegevoegd. Er wordt gecontroleerd of de huidige actie mogelijk is in de huidige status van de applicatie. Als de huidige actie niet mogelijk is, wordt er een status 151 gegenereerd (regel 40), wat ervoor zorgt dat de huidige actie wordt omgeleid naar de weergave van onverwachte fouten;
35.2.6. Het nieuwe antwoord HTML

De huidige wijzigingen hebben alleen betrekking op de sessies HTML. De sessies jSON of XML worden hierdoor niet beïnvloed. De klasse [HtmlResponse] verandert als volgt:
# afhankelijkheden
from flask import make_response, redirect, render_template
from flask.wrappers import Response
from flask_api import status
from flask_wtf.csrf import generate_csrf
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"]
# er wordt nagegaan of de status is gegenereerd door een actie ASV
# in welk geval een weergave moet worden getoond
asv_configs = config['mvc']["asv"]
trouvé = False
i = 0
# de lijst met weergaven wordt doorlopen
nb_views = len(asv_configs)
while not trouvé and i < nb_views:
# weergave nr. i
asv_config = asv_configs[i]
# rapporten die bij weergave nr. i horen
états = asv_config["états"]
# bevindt het gezochte rapport zich onder de rapporten die bij weergave nr. i horen
if état in états:
trouvé = True
else:
# volgende weergave
i += 1
# Gevonden?
if trouvé:
# dit is een actie ASV – er moet een weergave worden getoond waarvan het model al in de sessie aanwezig is
# de code HTML van het antwoord wordt gegenereerd
html = render_template(asv_config["view_name"], modèle=session['modèle'])
# het antwoord HTTP wordt samengesteld
response = make_response(html)
response.headers['Content-Type'] = 'text/html; charset=utf-8'
# het resultaat wordt weergegeven
return response, status_code
# niet gevonden – dit is een statuscode van een actie ADS
# hierop volgt een omleiding
redirected = False
for ads in config['mvc']['ads']:
# statussen die een omleiding vereisen
états = ads["états"]
if état in états:
# er vindt een omleiding plaats
redirected = True
break
# omleidingswoordenboek voor het geval van onverwachte fouten
if not redirected:
ads = config['mvc']['view_erreurs']
# is dit een omleiding naar een actie ASD of ASV?
# als er een sjabloon is, dan gaat het om een omleiding naar een actie ASV
# dan moet het sjabloon van de weergave V worden berekend die door de actie ASV wordt weergegeven
model_for_view = ads.get("model_for_view")
if model_for_view:
# berekening van het model van de volgende weergave
modèle = model_for_view.get_model_for_view(request, session, config, résultat)
# het model wordt in de sessie opgeslagen voor de volgende weergave
session['modèle'] = modèle
# nu moet de omleidings-URL worden gegenereerd, zonder het token CSRF te vergeten indien dit wordt gevraagd
if config['parameters']['with_csrftoken']:
csrf_token = f"/{generate_csrf()}"
else:
csrf_token = ""
# omleidingsantwoord
return redirect(f"{ads['to']}{csrf_token}"), status.HTTP_302_FOUND
- regels 18-35: er wordt nagegaan of de status die door de laatst uitgevoerde actie is gegenereerd, overeenkomt met die van een actie ASV;
- regels 36-46: zo ja, dan wordt de weergave V die bij de actie ASV hoort, weergegeven met als sjabloon het sjabloon dat in de sessie is gevonden en gekoppeld is aan de sleutel [‘modèle’];
- regels 48-60: wanneer we hier aankomen, weten we dat de status die door de laatst uitgevoerde actie is gegenereerd, die van actie ADS is. Dit zal vervolgens een omleiding veroorzaken. We zoeken in het configuratiebestand naar de definitie hiervan;
- regel 62: wanneer we hier zijn, hebben we de configuratie van de uit te voeren omleiding. Er zijn twee gevallen:
- het gaat om een omleiding naar een andere actie ADS. Er hoeft dan geen weergavesjabloon te worden berekend;
- het betreft een omleiding naar een actie ASV. Er moet dan een weergavemodel worden berekend (regels 67-68). Dit model wordt vervolgens in de sessie opgeslagen (regel 70);
- regels 72-76: de omleidings-URL wordt berekend;
- regels 78-79: het omleidingsantwoord wordt naar de client verzonden;
35.3. Tests
Voer de volgende tests uit met een browser:
- gebruik de applicatie op de gebruikelijke manier. Controleer of de enige URL-codes die door de browser worden weergegeven, URL, ASV en [/afficher-vue-nom_de_la_vue] zijn;
- ververs de pagina’s (F5) en controleer of dezelfde pagina vervolgens opnieuw wordt weergegeven. Er treden geen ongewenste bijwerkingen op;
Gebruik overigens de client [impots/http-clients/09]. Aangezien de aangebrachte wijzigingen alleen betrekking hebben op de sessies HTML, moeten de clients [main, main2, main3, Test1HttpClientDaoWithSession, Test2HttpClientDaoWithSession] blijven functioneren.
Laten we nu eens kijken naar een geval waarin de actie niet mogelijk is. De volgende weergave wordt getoond:

In plaats van [1] typen we URL [/supprimer-simulation/1]. De actie [/supprimer-simulation] maakt geen deel uit van de acties die in het scherm worden aangeboden, namelijk acties 1-4. Deze actie wordt daarom geweigerd. Het antwoord van de server is als volgt:
