Skip to content

36. Toepassingsopgave: versie 16

Image

36.1. Inleiding

De URL van onze applicatie hebben momenteel de vorm [/action/param1/param2/…]. We zouden graag een voorvoegsel aan deze URL willen toevoegen. Met bijvoorbeeld het voorvoegsel [/do] zouden we URL krijgen in de vorm [/do/action/param1/param2/…].

36.2. De nieuwe configuratie van de routes

Image

  • in [2], zal de routeberekening worden aangepast;
  • bij [1] wordt het bestand [config] aangepast om deze wijziging weer te geven;

De configuratie [config] wordt 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)

    # databaseconfiguratie
    import database
    config["database"] = database.configure(config)

    # instantiëring 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))

    # configuratie van routes
    import routes
    routes.configure(config)

    # de configuratie wordt doorgegeven
    return config
  • regels 37-39: de module [routes] (regel 38) zorgt voor de configuratie van de routes (regel 39);

Het bestand met routes zonder token CSRF [configs/routes_without_csrftoken] verandert als volgt:


# afhankelijkheden

from flask import redirect, request, session, url_for
from flask_api import status

# configuratie van de applicatie
config = {}

# de frontcontroller
def front_controller() -> tuple:
    # het verzoek wordt doorgestuurd naar de hoofdcontroller
    main_controller = config['mvc']['controllers']['main-controller']
    return main_controller.execute(request, session, config)

# hoofdmap van de applicatie
def index() -> tuple:
    # omleiding naar /init-session/html
    return redirect(url_for("init_session", type_response="html"), status.HTTP_302_FOUND)

# init-session
def init_session(type_response: str) -> tuple:
    # de bij de actie behorende controller wordt uitgevoerd
    return front_controller()

# gebruikersauthenticatie
def authentifier_utilisateur() -> tuple:
    # de bij de actie behorende controller wordt uitgevoerd
    return front_controller()

# belasting-berekenen
def calculer_impot() -> tuple:
    # de bij de actie behorende controller wordt uitgevoerd
    return front_controller()


De routes zijn uit het bestand verwijderd. Alleen de bijbehorende functies zijn overgebleven.

De routebestanden met token CSRF en [configs/routes_with_csrftoken] ondergaan hetzelfde lot:


# afhankelijkheden

from flask import redirect, request, session, url_for
from flask_api import status
from flask_wtf.csrf import generate_csrf

# configuratie
config = {}

# de frontcontroller
def front_controller() -> tuple:
    # het verzoek wordt doorgestuurd naar de hoofdcontroller
    main_controller = config['mvc']['controllers']['main-controller']
    return main_controller.execute(request, session, config)

# hoofdmap van de applicatie
def index() -> tuple:
    # omleiding naar /init-session/html
    return redirect(url_for("init_session", type_response="html"csrf_token=generate_csrf()), status.HTTP_302_FOUND)

# init-session
def init_session(type_response: str, csrf_token: str) -> tuple:
    # de bij de actie behorende controller wordt uitgevoerd
    return front_controller()

# gebruiker authenticeren
def authentifier_utilisateur(csrf_token: str) -> tuple:
    # de bij de actie behorende controller wordt uitgevoerd
    return front_controller()



# init-session-without-csrftoken voor json- en xml-clients
def init_session_without_csrftoken(type_response: str) -> tuple:
    # omleiding naar /init-session/type_response
    return redirect(url_for("init_session", type_response=type_response, csrf_token=generate_csrf()),
                    status.HTTP_302_FOUND)

De routes worden berekend door de volgende module: [configs/routes]:


from flask import Flask

def configure(config: dict):
    # configuratie van routes

    # Flask-applicatie
    app = Flask(__name__, template_folder="../flask/templates", static_folder="../flask/static")
    config['app'] = app

    # routes van de webapplicatie importeren
    if config['parameters']['with_csrftoken']:
        import routes_with_csrftoken as routes
    else:
        import routes_without_csrftoken as routes

    # de configuratie wordt in de routes geïnjecteerd
    routes.config = config

    # het voorvoegsel van de URL van de applicatie
    prefix_url = config["parameters"]["prefix_url"]

    # token CSRF
    with_csrftoken = config["parameters"]['with_csrftoken']
    if with_csrftoken:
        csrftoken_param = f"/<string:csrf_token>"
    else:
        csrftoken_param = ""

    # de routes van de Flask-applicatie
    # de root van de applicatie
    app.add_url_rule(f'{prefix_url}/', methods=['GET'],
                     view_func=routes.index)

    # init-session
    app.add_url_rule(f'{prefix_url}/init-session/<string:type_response>{csrftoken_param}', methods=['GET'],
                     view_func=routes.init_session)

    # sessie-initialiseren-zonder-csrf-token
    if with_csrftoken:
        app.add_url_rule(f'{prefix_url}/init-session-without-csrftoken/<string:type_response>',
                         methods=['GET'],
                         view_func=routes.init_session_without_csrftoken)

    # gebruiker-authenticeren
    app.add_url_rule(f'{prefix_url}/authentifier-utilisateur{csrftoken_param}', methods=['POST'],
                     view_func=routes.authentifier_utilisateur)

    # belasting-berekenen
    app.add_url_rule(f'{prefix_url}/calculer-impot{csrftoken_param}', methods=['POST'],
                     view_func=routes.calculer_impot)

    # belasting in batches berekenen
    app.add_url_rule(f'{prefix_url}/calculer-impots{csrftoken_param}', methods=['POST'],
                     view_func=routes.calculer_impots)

    # simulaties weergeven
    app.add_url_rule(f'{prefix_url}/lister-simulations{csrftoken_param}', methods=['GET'],
                     view_func=routes.lister_simulations)

    # simulatie verwijderen
    app.add_url_rule(f'{prefix_url}/supprimer-simulation/<int:numero>{csrftoken_param}', methods=['GET'],
                     view_func=routes.supprimer_simulation)

    # sessie-beëindigen
    app.add_url_rule(f'{prefix_url}/fin-session{csrftoken_param}', methods=['GET'],
                     view_func=routes.fin_session)

    # belastingberekening weergeven
    app.add_url_rule(f'{prefix_url}/afficher-calcul-impot{csrftoken_param}', methods=['GET'],
                     view_func=routes.afficher_calcul_impot)

    # beheergegevens ophalen
    app.add_url_rule(f'{prefix_url}/get-admindata{csrftoken_param}', methods=['GET'],
                     view_func=routes.get_admindata)

    # belastingberekening-weergave
    app.add_url_rule(f'{prefix_url}/afficher-vue-calcul-impot{csrftoken_param}', methods=['GET'],
                     view_func=routes.afficher_vue_calcul_impot)

    # weergave-authenticatie
    app.add_url_rule(f'{prefix_url}/afficher-vue-authentification{csrftoken_param}', methods=['GET'],
                     view_func=routes.afficher_vue_authentification)

    # overzicht-simulaties-weergeven
    app.add_url_rule(f'{prefix_url}/afficher-vue-liste-simulations{csrftoken_param}', methods=['GET'],
                     view_func=routes.afficher_vue_liste_simulations)

    # weergave-foutenlijst
    app.add_url_rule(f'{prefix_url}/afficher-vue-liste-erreurs{csrftoken_param}', methods=['GET'],
                     view_func=routes.afficher_vue_liste_erreurs)

    # speciaal geval
    if with_csrftoken:
        # sessie-initialiseren-zonder-csrftoken voor json- en xml-clients
        app.add_url_rule(f'{prefix_url}/init-session-without-csrftoken', methods=['GET'],
                         view_func=routes.init_session_without_csrftoken)
  • regels 6-8: de Flask-applicatie wordt aangemaakt en in de configuratie opgenomen;
  • regels 10-14: het routebestand dat bij de situatie past, wordt geïmporteerd. We moeten niet vergeten dat het geïmporteerde bestand in feite geen routes bevat. Het bevat alleen de functies die aan deze routes zijn gekoppeld;
  • regel 17: de functies die bij de routes horen, moeten de configuratie van de applicatie kennen;
  • regel 20: we noteren het voorvoegsel van de URL. Dit kan leeg zijn;
  • regels 22-27: routes met het token CSRF hebben een extra parameter ten opzichte van routes die dit token niet hebben. Om dit verschil te verwerken, gebruiken we de variabele [csrftoken_param]:
    • deze bevat de lege tekenreeks als er geen token CSRF in de routes voorkomt;
    • deze bevat de tekenreeks [/<string:csrf_token>] als er een token CSRF is;
  • regels 29-96: elke route van de applicatie is gekoppeld aan een functie uit het geïmporteerde routenbestand (regels 10-14);

36.3. De nieuwe controllers

Image

In de vorige versie haalden alle controllers de actie die op dat moment werd verwerkt op de volgende manier op:


    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]

Deze code werkt niet meer als er een voorvoegsel is, bijvoorbeeld [/do/lister-simulations]. In dat geval zou de actie, regel 3, [do] zijn en dus onjuist.

We zetten deze code als volgt om:


    def execute(self, request: LocalProxy, session: LocalProxy, config: dict) -> (dict, int):
        # de padelementen worden opgehaald
        prefix_url = config["parameters"]["prefix_url"]
        params = request.path[len(prefix_url):].split('/')
        action = params[1]

Dit doen we in alle controllers.

36.4. De nieuwe modellen

Image

In de vorige versie genereerde elke sjabloonklasse een sjabloon op de volgende manier:


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 = {}
        # status van de applicatie
        état = résultat["état"]
        # het sjabloon is afhankelijk van de status
        

        # CSRF-token
        modèle['csrf_token'] = super().get_csrftoken(config)

        # mogelijke acties vanuit de weergave
        modèle['actions_possibles'] = []

        # het sjabloon wordt weergegeven
        return modèle

Voortaan krijgt het sjabloon een extra sleutel, het voorvoegsel van de URL:


def get_model_for_view(self, request: Request, session: LocalProxy, config: dict, résultat: dict) -> dict:
        # de gegevens van de pagina worden in het model ingekapseld
        modèle = {}
        # status van de applicatie
        état = résultat["état"]
        # het model is afhankelijk van de status
        

        # CSRF-token
        modèle['csrf_token'] = super().get_csrftoken(config)

        # mogelijke acties vanuit de weergave
        modèle['actions_possibles'] = []
    
        # voorvoegsel van de URL
        modèle["prefix_url"] = config["parameters"]["prefix_url"]

        # het model wordt teruggestuurd
        return modèle

36.5. De nieuwe fragmenten

Image

Alle fragmenten die URL bevatten, moeten worden aangepast.

Het fragment [v-authentification]


<!-- formulier HTML - we verzenden de waarden met de actie [authentifier-utilisateur] -->
<form method="post" action="{{modèle.prefix_url}}/authentifier-utilisateur{{modèle.csrf_token}}">

    <!-- titel -->
    <div class="alert alert-primary" role="alert">
        <h4>Veuillez vous authentifier</h4>
    </div>


</form>

Het fragment [v-calcul-impot]


<!-- formulier HTML verzonden -->
<form method="post" action="{{modèle.prefix_url}}/calculer-impot{{modèle.csrf_token}}">
    <!-- bericht met 12 kolommen op een blauwe achtergrond -->
    

</form>

Het fragment [v-liste-simulations]




{% if modèle.simulations is defined and modèle.simulations|length!=0 %}


<!-- simulatietabel -->
<table class="table table-sm table-hover table-striped">
    
    <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="{{modèle.prefix_url}}/supprimer-simulation/{{simulation.id}}{{modèle.csrf_token}}">Supprimer</a></td>
    </tr>
    {% endfor %}
    </tr>
    </tbody>
</table>
{% endif %}

Het fragment [v-menu]


<!-- 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="{{modèle.prefix_url}}{{optionMenu.url}}{{modèle.csrf_token}}">{{optionMenu.text}}</a>
    {% endfor %}
</nav>

36.6. Het nieuwe antwoord HTML

Image

In de vorige versie eindigde de code van het antwoord HTML met een omleiding:


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 hangt af van de statuscode die door de controller wordt geretourneerd
        état = résultat["état"]

        

        # nu moet de omleidings-URL worden gegenereerd, zonder het token CSRF te vergeten als daar om 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

Deze code is nu als volgt:


def build_http_response(self, request: LocalProxy, session: LocalProxy, config: dict, status_code: int,
                            résultat: dict) -> (Response, int):
        

        # nu moet de omleidingscode URL worden gegenereerd, zonder het token CSRF te vergeten als daarom wordt gevraagd
        if config['parameters']['with_csrftoken']:
            csrf_token = f"/{generate_csrf()}"
        else:
            csrf_token = ""

        # omleidingsantwoord
        return redirect(f"{config['parameters']['prefix_url']}{ads['to']}{csrf_token}"), status.HTTP_302_FOUND

36.7. Tests

Image

Laten we het volgende voorvoegsel in het configuratiebestand [parameters] plaatsen:



        # CSRF-token
        "with_csrftoken"True,
        # beheerde databases MySQL (mysql), PostgreSQL (pgres)
        "databases"["mysql""pgres"],
        # voorvoegsel van de URL van de applicatie
        # stel de tekenreeks in op leeg als er geen voorvoegsel gewenst is, anders /voorvoegsel
        "prefix_url""/do",

Laten we de applicatie starten en vervolgens URL [http://localhost:5000/do] opvragen. Het antwoord is als volgt:

Image

  • in [1], het voorvoegsel van de URL;
  • in [2], het token CSRF;

De applicatie kan ook worden getest met de consoletests van [http-clients/09]:

Image

In de configuratiebestanden [1] en [2] moet het voorvoegsel van URL worden opgenomen in het URL van de server:


        "server": {
            # "urlServer": "http://127.0.0.1:5000",
            "urlServer""http://127.0.0.1:5000/do",
            "user": {
                "login""admin",
                "password""admin"
            },
            "url_services": {
                
            }
  • regel 3: de URL van de server bevat nu het voorvoegsel van de URL;

Zodra deze wijziging is doorgevoerd, zouden alle consoletests moeten werken.