Validaciones¶
Introducción¶
Las validaciones en una API son procesos que verifican que los datos enviados al servidor cumplen con ciertas reglas o criterios antes de ser procesados. Esto asegura que la información sea correcta, completa y segura.
Importancia¶
- Prevención de errores: Evita que datos incorrectos causen fallos en el sistema.
- Seguridad: Protege contra inyecciones de código y otros ataques.
- Integridad de datos: Garantiza que los datos almacenados y procesados sean consistentes y confiables.
- Experiencia del usuario: Proporciona retroalimentación inmediata sobre errores en los datos enviados.
¡Esencial para un funcionamiento robusto y seguro de la API!
Uso¶
En Cafeto, los DTO ejecutarán las validaciones que Pydantic tiene disponibles por defecto.
| Python | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 | |
En el ejemplo anterior, los campos username, password, confirm_password y birth_date son obligatorios, mientras que el campo name es opcional, y el campo username tiene un límite mínimo de 3 caracteres. Estas validaciones se ejecutarán automáticamente cuando se consuma la acción y los errores se retornarán con un statusCode 400.
Para las validaciones personalizadas, existe el decorador @validate. Este decorador recibe como parámetro el nombre del campo a validar; si no se provee este campo, se validará todo el modelo.
| Python | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | |
-
Valida el campo
password. -
Valida el campo
birth_date. -
Valida todo el modelo.
El decorador @validate es similar a @field_validator de Pydantic y se desarrolló con el fin de poder crear validaciones personalizadas y asíncronas. También soporta inyección de dependencias en el método sobre el cual se aplica.
| Bash | |
|---|---|
1 2 3 4 5 6 7 8 | |
Si enviamos la solicitud anterior, obtendremos el siguiente resultado:
| JSON | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | |
Este es más o menos el mismo formato en que Pydantic retorna la validación de los datos.
FieldError se usa para lanzar una excepción cuando falla un campo del DTO y recibe como parámetro un objeto Error, que a su vez recibe los parámetros type: str y msg: str. Existe un tercer parámetro llamado loc: List[str | int] | str | int; si este no se usa, Pydantic lo hará automáticamente.
En el arreglo loc, la palabra __model__ hace referencia a errores globales, es decir, que no están necesariamente asociados a ningún campo del DTO.
Información importante¶
Es importante notar que estos métodos son estáticos, por lo que no reciben el parámetro self.
Los parámetros del método donde se aplica el decorador @validate son: value y data.
-
value: Es el valor actual del campo que se está validando y el tipo de dato será el que se configuró desde el modelo.
Nota: Si el validador aplica para todo el modelo, el primer parámetro será un diccionario con los datos de todo el modelo.
-
data: Es un diccionario con los demás campos del modelo.
Nota: Si el validador aplica para todo el modelo, el segundo parámetro no existe.
Como regla general, si no se necesita hacer uso del segundo parámetro, se suele llamar "_" (guion bajo).
| Python | |
|---|---|
1 2 | |
Formato de los errores¶
Existe una forma adicional de retornar errores; para ello, se debe configurar la aplicación para que los retorne con este formato.
| Python | |
|---|---|
1 2 3 4 | |
En ese caso, los errores se lanzarán con los dos formatos, el anteriormente visto y el nuevo.
| JSON | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | |
En este caso, se lanzan dos tipos de errores: errorList y errorObject. En este último, el campo loc deja de existir y se convierte en las llaves anidadas del objeto con los errores.
Modificar valores¶
El decorador @validate también sirve para modificar los valores del DTO al ser devueltos, es decir, no solo se pueden usar para validar datos sino para alterar los valores de estos.
Nota: Es importante anotar que los validadores siempre deben devolver un valor, este valor será el que finalmente se usará en el modelo.
| Python | |
|---|---|
1 2 3 4 5 6 | |
El valor del campo name será el valor asignado + " - Hello".
Modelos anidados¶
Si se requiere validar un modelo, donde uno de sus campos es otro modelo, debe poner atención en la forma como estos se validan. Si usamos el decorador @validate sobre el campo que contiene el modelo anidado, no se realizarán las validaciones personalizadas de este. Esto se debe a que el sistema debe determinar cuál validación se debe ejecutar y se dará prioridad a aquella que esta menos inmersa en el modelo.
| Python | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
En este último ejemplo, la validación validate_name_a y ninguna otra que se encuentre en el modelo MyModelA se ejecutará, debido a que la validación validate_model_a_from_b del modelo MyModelB la está sobrescribiendo.
Validadores¶
Como se puede observar en los ejemplos anteriores, las validaciones están asociadas a los DTO. Sin embargo, es posible que no siempre se desee este comportamiento. Para estos casos, existen los validadores. Estos son clases a los que también se les aplica el decorador @validate, ya sea para validar un campo o todo el modelo, al igual que los DTO. Para utilizar estos validadores, solo es necesario heredar de la clase BaseValidator.
| Python | |
|---|---|
1 2 3 4 5 6 7 8 | |
A los validadores también se les pueden inyectar dependencias, al igual que a los DTO.
| Python | |
|---|---|
1 2 3 4 5 6 7 8 9 | |
Para hacer uso del validador, este debe configurarse en la acción del controlador. Esto se realiza en el parámetro body de los decoradores @app.get, @app.post, @app.put, @app.patch y @app.delete.
| Python | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
En este ejemplo, las validaciones propias del DTO, como los campos requeridos (name, age), serán aplicados. Además, las validaciones personalizadas se ejecutarán utilizando el validador MyValidator. Esto es particularmente útil si se desea mantener un DTO más limpio y reutilizar dichas validaciones entre diferentes DTO.
Es posible tener validadores anidados, al igual que los DTO.
| Python | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
No importa si el campo a validar es una lista o un diccionario; la configuración permanece igual.
| Python | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
También existe la posibilidad de no validar el DTO en absoluto y delegar esta tarea a un proceso manual. Para ello, se debe enviar None en la propiedad body en lugar del validador.
| Python | |
|---|---|
1 2 3 4 5 | |
En este caso, el parámetro request contendrá los valores enviados mediante el método POST, pero sin validaciones.
El DTO puede ser validado posteriormente mediante el siguiente código:
| Python | |
|---|---|
1 2 3 4 5 6 7 8 9 10 | |
Si se desea usar un validador diferente al DTO, este se debe pasar como parámetro al método check.
| Python | |
|---|---|
1 2 3 4 | |
Es posible retornar estos errores tal y como los genera la excepción al momento de hacer la validación del modelo, o formatearlos para que coincidan con el formato que normalmente se utiliza cuando las validaciones se realizan de forma automática.
| Python | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |