Capítulo 3.6: Manejo de errores y validación
Tiempo de estudio: 50 minutos
1. Manejo de errores: Los "escudos de emergencia" de una nave espacial
Incluso en la nave más perfecta, pueden ocurrir situaciones imprevistas:
- Comando incorrecto del Centro de Control: El cliente envió datos incorrectos.
- Pérdida de conexión con el módulo: Recurso no encontrado en la base de datos.
- Fallo en el reactor: Error interno del servidor.
El manejo adecuado de errores es un sistema de "escudos de emergencia". Evita que la nave se desintegre y, en su lugar, envía una señal clara al Centro de Control sobre lo que salió mal.
💡 Analogía espacial:
En lugar de simplemente transmitir una señal de "¡ACCIDENTE!" al Centro de Control, una buena computadora de a bordo enviará un informe estructurado:
Esto permite a los ingenieros en la Tierra comprender rápidamente el problema y tomar medidas.
2. Validación Pydantic: La "computadora de a bordo" integrada
Ya nos hemos encontrado con la magia de Pydantic. Si intentas crear una nave con un tipo de datos incorrecto (por ejemplo, launch_year
como cadena de texto), FastAPI devolverá automáticamente un error 422 Unprocessable Entity
con una descripción detallada de qué campo y por qué no pasó la validación.
Ejemplo de solicitud a POST /spaceships
:
{
"name": "X-Wing",
"type": "Caza",
"launch_year": "hace mucho tiempo", // <-- ¡Tipo incorrecto!
"status": "En servicio"
}
Respuesta automática de FastAPI:
{
"detail": [
{
"loc": [
"body",
"launch_year"
],
"msg": "el valor no es un entero válido",
"type": "type_error.integer"
}
]
}
3. Manejo de "Recurso no encontrado": Excepción HTTPException
Ya hemos utilizado esto en las operaciones CRUD. HTTPException
es la forma estándar de FastAPI de interrumpir la ejecución de una solicitud y devolver inmediatamente una respuesta de error al cliente.
Recordemos el código de GET /spaceships/{ship_id}
:
# main.py
from fastapi import FastAPI, HTTPException # Asegúrate de que HTTPException esté importado
# ...
@app.get("/spaceships/{ship_id}", response_model=Spaceship, tags=["Naves espaciales"])
def get_spaceship(ship_id: int):
ship = db_spaceships.get(ship_id)
if not ship:
# Si la nave no se encuentra, "lanzamos" una excepción 404
raise HTTPException(status_code=404, detail=f"Nave espacial con ID {ship_id} no encontrada")
return ship
raise HTTPException(...)
: Esta llamada detiene la ejecución de la función.status_code=404
: Establece el estado HTTP de la respuesta.detail
: Mensaje que se enviará al cliente en el cuerpo de la respuesta JSON.
4. Validadores personalizados: "Verificaciones especiales" antes del lanzamiento
¿Qué pasa si queremos añadir nuestra propia lógica de negocio más compleja? Por ejemplo, prohibir el lanzamiento de naves con el nombre "Estrella de la Muerte".
Para ello, Pydantic cuenta con una poderosa herramienta: los validadores.
Paso 1: Añadir un validador al modelo SpaceshipCreate
# main.py
from pydantic import BaseModel, Field, validator
class SpaceshipCreate(BaseModel):
name: str = Field(..., min_length=3, max_length=50)
type: str
launch_year: int = Field(..., gt=1950)
status: str
@validator('name')
def name_must_not_be_forbidden(cls, v):
"""Verifica que el nombre de la nave no esté en la lista de nombres prohibidos."""
if 'Death Star' in v:
raise ValueError('¡Nombres como "Estrella de la Muerte" están prohibidos por decreto imperial!')
return v.title() # De paso, ponemos la primera letra del nombre en mayúscula
@validator('name')
: Un decorador que "vincula" esta función al camponame
.cls, v
: El método recibe la clase misma (cls
) y el valor del campo (v
).raise ValueError(...)
: Si la validación falla, lanzamos una excepción estándar de Python. FastAPI la interceptará y la convertirá en un bonito error422
.return v.title()
: Si todo está bien, debemos devolver el valor obligatoriamente. Incluso podemos modificarlo sobre la marcha (por ejemplo, convertirlo a un formato estándar).
Paso 2: Probar
Reinicia uvicorn
e intenta crear una nave con un nombre prohibido a través de /docs
. ¡Recibirás un error 422
con tu mensaje personalizado!
5. Manejo global de errores: El "protocolo de emergencia" de la estación
A veces es necesario interceptar errores inesperados (por ejemplo, un fallo de conexión a una base de datos real) y devolver un formato de respuesta único y estandarizado.
Para ello se utiliza el decorador @app.exception_handler
.
Ejemplo: Interceptación de todos los errores ValueError
# main.py
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
# ...
@app.exception_handler(ValueError)
async def value_error_exception_handler(request: Request, exc: ValueError):
"""
Manejador global para todos los errores ValueError,
para devolver un JSON estandarizado.
"""
return JSONResponse(
status_code=400,
content={"message": f"Error en los datos: {str(exc)}"},
)
@app.exception_handler(ValueError)
: Le dice a FastAPI que esta función debe manejar todos losValueError
que no hayan sido interceptados previamente.async def ...
: Los manejadores de excepciones deben ser asíncronos (async
).JSONResponse
: Permite un control total sobre el cuerpo y el estado de la respuesta.
Ahora, cuando nuestro validador personalizado se active, la respuesta tendrá un formato más amigable que el que hemos definido.
Cuestionario de repaso
🚀 Resumen del capítulo:
Ha instalado en su nave API un potente sistema de seguridad y protocolos de emergencia. Ahora puede:
- 🛡️ Repeler automáticamente los ataques de "datos incorrectos" utilizando Pydantic.
- 🚨 Informar correctamente sobre la ausencia de recursos (
404 Not Found
) a través deHTTPException
. - ⚙️ Realizar "validaciones especiales" utilizando validadores personalizados.
- 🧯 Interceptar globalmente fallos inesperados y proporcionar respuestas estandarizadas.
¡Su "hiperimpulsor" no solo es rápido, sino también increíblemente fiable!
📌 Verificación:
- Intente crear una nave con el nombre "Death Star" y asegúrese de recibir un error
400
con su mensaje personalizado.- Intente solicitar
GET /spaceships/999
y asegúrese de recibir un error404
.- Intente enviar una solicitud
POST
conlaunch_year
como una cadena de texto y asegúrese de recibir un error422
.⚠️ Si hay errores:
- Asegúrese de que todos los módulos necesarios (
HTTPException
,validator
,Request
,JSONResponse
) estén importados.- Verifique que los decoradores
@validator
y@app.exception_handler
estén escritos sin errores tipográficos.
¡Enhorabuena por completar el Capítulo 3! Ha construido y lanzado desde cero una API potente, documentada y protegida con FastAPI. Está listo para emprender auténticas misiones espaciales.