Skip to content

38. Ejercicio práctico: versión 18

38.1. Implementación

Image

El archivo [impots/http-servers/13] se obtiene inicialmente al copiar el archivo [impots/http-servers/12] y luego se modifica parcialmente.

Comenzamos agregando un nuevo parámetro en el archivo [configs/parameters]:


…        
         # token CSRF
        "with_csrftoken"False,
        # bases de datos administradas: MySQL (MySQL), PostgreSQL (PostgreSQL)
        "databases"["mysql""pgres"],
        # prefijo de los URL de la aplicación
        # deja la cadena vacía si no quieres un prefijo o, de lo contrario, usa /prefijo
        "prefix_url""/do",
        # URL raíz del servidor Apache; deja la cadena vacía para ejecutarlo fuera de Apache
        "application_root""/impots"

En la línea 10, el parámetro [application_root] representará el alias WSGI del servidor virtual Apache.

Con este parámetro, podemos corregir la instrucción de [responses/HtmlResponse] que había provocado el error:

Image



# Ahora hay que generar el URL de redirección sin olvidar el token CSRF si se solicita
        if config['parameters']['with_csrftoken']:
            csrf_token = f"/{generate_csrf()}"
        else:
            csrf_token = ""

        # respuesta de redirección
        return redirect(f"{config['parameters']['application_root']}{config['parameters']['prefix_url']}{ads['to']}{csrf_token}")
, status.HTTP_302_FOUND
  • línea 9: hemos agregado la raíz de la aplicación al inicio del destino de la redirección URL;

También debemos corregir todos los fragmentos para que los URL que contienen comiencen con la raíz de la aplicación (o alias WSGI):

Image

El fragmento [v-authentification]


<!-- formulario HTML: se envían sus valores con la acción [authentifier-utilisateur] -->
<form method="post" action="{{modèle.application_root}}{{modèle.prefix_url}}/authentifier-utilisateur{{modèle.csrf_token}}"
>

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


</form>

El fragmento [v-calcul-impot]


<!-- formulario HTML enviado -->
<form method="post" action="{{modèle.application_root}}{{modèle.prefix_url}}/calculer-impot{{modèle.csrf_token}}">
    <!-- mensaje en 12 columnas sobre fondo azul -->
    

</form>

El fragmento [v-liste-simulations]




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


<!-- tabla de simulaciones -->
<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.application_root}}{{modèle.prefix_url}}/supprimer-simulation/{{simulation.id}}{{modèle.csrf_token}}">Supprimer</a></td>
    </tr>
    {% endfor %}
    </tr>
    </tbody>
</table>
{% endif %}

El fragmento [v-menu]


<!-- menú Bootstrap -->
<nav class="nav flex-column">
    <!-- visualización de una lista de enlaces HTML -->
    {% for optionMenu in modèle.optionsMenu %}
    <a class="nav-link" href="{{modèle.application_root}}{{modèle.prefix_url}}{{optionMenu.url}}{{modèle.csrf_token}}">{{optionMenu.text}}</a>
    {% endfor %}
</nav>

Todos los fragmentos anteriores utilizan la plantilla [modèle.application_root]. Por el momento, la clave [application_root] no existe en las plantillas generadas por las clases de plantillas.

Image

La clase [AbstractBaseModelForView], que es la clase padre de todas las clases que generan una plantilla, queda así:


from abc import abstractmethod

from flask import Request
from flask_wtf.csrf import generate_csrf
from werkzeug.local import LocalProxy

from InterfaceModelForView import InterfaceModelForView

class AbstractBaseModelForView(InterfaceModelForView):

    @abstractmethod
    def get_model_for_view(self, request: Request, session: LocalProxy, config: dict, résultat: dict) -> dict:
        pass

    def update_model(self, modèle: dict, config: dict):
        # cálculo del token CSRF
        if config['parameters']['with_csrftoken']:
            csrf_token = f"/{generate_csrf()}"
        else:
            csrf_token = ""
        # actualización de la plantilla pasada como parámetro
        modèle.update({
            # token CSRF
            'csrf_token': csrf_token,
            # prefix_url
            'prefix_url': configQZXW2HTMLBWyJwYXJhbWV0ZXJzIl0ZQXQZXW2HTMLBWyJwcmVmaXhfdXJsIl0ZQX,
            # application_root
            'application_root': configQZXW2HTMLBWyJwYXJhbWV0ZXJzIl0ZQXQZXW2HTMLBWyJhcHBsaWNhdGlvbl9yb290Il0ZQX,
        })
  • línea 15: el método [update_model] tiene la función de incluir vistas en la plantilla:
    • línea 24: el token CSRF;
    • línea 26: el prefijo de URL;
    • línea 28: la raíz de la aplicación o alias WSGI;

Las cuatro clases hijas invocan a la clase padre con el siguiente código:



        # acciones posibles desde la vista
        modèle['actions_possibles'] = ["afficher-vue-authentification""authentifier-utilisateur"]

        # finalización del modelo por parte de la clase padre
        super().update_model(modèle, config)

        # se devuelve el modelo
        return modèle
  • línea 6: cada clase hija llama a su clase padre para actualizar el modelo que ha creado;

La versión 18 está lista. Retomamos los dos servidores virtuales de Apache de la versión 17 y los modificamos:

Image

Los dos archivos [flask-impots-withXX.conf] solo se modifican en un punto:


# carpeta del script .wsgi
define ROOT "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/13/apache"

# nombre del sitio web configurado por este archivo
# aquí se llamará flask-impots-withmysql
# los URL serán del tipo http(s)://flask-impots-withmysql/ruta
define SITE "flask-impots-withmysql"

# Agrega la dirección IP 127.0.0.1 para el sitio SITE en c:/windows/system32/drivers/etc/hosts

# Agrega aquí las rutas de las bibliotecas de Python que se van a utilizar; sepáralas con comas
# aquí las bibliotecas de un entorno virtual de Python
WSGIPythonPath  "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages"

# Directorio principal de Python: solo es necesario si hay varias versiones de Python instaladas
# WSGIPythonHome "C:/Archivos de programa/Python38"

# URL HTTP
<VirtualHost *:80>
    # con el alias /, los URL tendrán el formato /{prefixe_url}/action/...
    # con el alias /impuestos, los URL tendrán el formato /impuestos/{prefixe_url}/acción/...
    # donde [prefixe_url] se define en parameters.py
    WSGIScriptAlias /impots "${ROOT}/main_withmysql.wsgi"
    …
</VirtualHost>

# URL protegidas con HTTPS
<VirtualHost *:443>
    # con el alias /, los URL tendrán el formato /{prefixe_url}/action/...
    # con el alias /impuestos, los URL tendrán el formato /impuestos/{prefixe_url}/action/...
    #, donde [prefixe_url] se define en parameters.py
    WSGIScriptAlias /impots "${ROOT}/main_withmysql.wsgi"
    ..

</VirtualHost>
  • en la línea 12, ahora se utiliza la carpeta [impots/http-servers/13/apache].

Estamos listos para las pruebas de la versión 18 portada a Apache. La configuración es la siguiente:

  • el alias WSGI es /impuestos en los dos archivos de configuración de los servidores virtuales;
  • en el archivo de configuración [configs/parameters], los parámetros son los siguientes:

        # token CSRF
        "with_csrftoken"False,
        # prefijo de la aplicación URL
        # deja la cadena vacía si no quieres un prefijo o, de lo contrario, usa /prefijo
        "prefix_url""/do",
        # URL raíz del servidor Apache; deja la cadena vacía para ejecutarlo fuera de Apache
        "application_root""/impots"

Se inician el servidor Apache y los dos SGBD. Se solicita el URL y el [https://flask-impots-withmysql/impots/do]. La respuesta del servidor es la siguiente:

Image

Se muestra correctamente la pantalla de autenticación que no habíamos podido ver en la versión anterior. El resto de la aplicación funciona con normalidad.

Ahora probamos el otro servidor virtual. Solicitamos URL y [https://flask-impots-withpgres/impots/do]. La respuesta del servidor es la siguiente:

Image

Los conceptos de alias WSGI y prefijo de URL cumplen la misma función. Uno de estos dos conceptos es redundante. Así, para anteponer la cadena [/impots/do] a los URL del servidor Apache, se puede proceder de tres maneras:

1 – [WGSIAlias /impots] y [prefix_url=’/do’];

2 – [WGSIAlias /] y [prefix_url=’/impots/do’];

3 – [WGSIAlias /impots/do] y [prefix_url=’’];

38.2. Pruebas de consola

Se vuelven a utilizar las pruebas de consola del cliente [http-clients/09]:

Image

  • el URL del servidor debe modificarse en las configuraciones [1] y [3];
  • se debe realizar una modificación en la capa [dao] para que sea compatible con el protocolo HTTPS del servidor Apache;

En los archivos [config], el URL del servidor queda así:


        "server": {
            # "urlServer": "http://127.0.0.1:5000",
            # "urlServer": "http://127.0.0.1:5000/do",
            "urlServer""https://flask-impots-withmysql/impots/do",
            "user": {
                "login""admin",
                "password""admin"
            },
            "url_services": {
                
            }
        },
        # modo de depuración
        "debug"True,
        # csrf_token
        "with_csrftoken"False,
  • línea 4: el nuevo URL del servidor. Por primera vez en este documento, el cliente utiliza el protocolo HTTPS;

La clase [ImpôtsDaoWihHttpSession] de la capa [dao] evoluciona de la siguiente manera:


…    
     # etapa de solicitud/respuesta
    def get_response(self, method: str, url_service: str, data_value: dict = None, json_value=None):
        # [method]: método HTTP, GET o POST
        # [url_service]: URL de servicio
        # [data]: parámetros del POST en x-www-form-urlencoded
        # [json]: parámetros de POST en json
        # [cookies]: cookies que se deben incluir en la solicitud

        #: se debe tener una sesión XML o JSON; de lo contrario, no se podrá gestionar la respuesta
        if self.__session_type not in ['json''xml']:
            raise ImpôtsError(73"il n'y a pas de session valide en cours")

        #: se agrega el token CSRF al URL de servicio
        if self.__csrf_token:
            url_service = f"{url_service}/{self.__csrf_token}"

        # ejecución de la solicitud
        response = requests.request(method,
                                    url_service,
                                    data=data_value,
                                    json=json_value,
                                    cookies=self.__cookies,
                                    allow_redirects=True,
                                    # para el protocolo https
                                    verify=False)

        # ¿modo de depuración?
        if self.__debug:
            # usuario
            if not self.__logger:
                self.__logger = self.__config['logger']
            # iniciar sesión
            self.__logger.write(f"{response.text}\n")

        

        # se devuelve el resultado
        return résultat['réponse']
  • En la línea 26, se agrega el parámetro [verify=False] debido al protocolo HTTPS utilizado por el servidor Apache. El módulo [requests] (línea 19) es capaz de manejar de forma nativa el protocolo HTTPS. Por defecto, verifica la validez del certificado de seguridad que le envía el servidor HTTPS y lanza una excepción si el certificado recibido no es válido. Este es el caso aquí, donde el servidor Apache de Laragon envía un certificado autofirmado. Para evitar la excepción, se utiliza el parámetro [verify=False] para indicarle al módulo [requests] que no genere una excepción. De esta manera, [requests] se limita a mostrar una advertencia (warning) en la consola.

Una vez realizadas estas modificaciones, todas las pruebas de consola deberían funcionar.