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 |
|
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 |
|