Leer y enviar cabeceras HTTP
Las cabeceras HTTP son metadatos que acompañan cada petición y respuesta en el protocolo HTTP. Funcionan como información adicional que describe aspectos técnicos de la comunicación, como el tipo de contenido, la codificación de caracteres o preferencias del cliente.
En FastAPI, trabajar con cabeceras es sencillo gracias a las utilidades integradas que nos permiten tanto leer cabeceras de las peticiones entrantes como enviar cabeceras personalizadas en nuestras respuestas.
Leer cabeceras de peticiones
Para acceder a las cabeceras que envía el cliente, utilizamos la clase Header
de FastAPI. Esta función nos permite extraer valores específicos de las cabeceras HTTP de forma similar a como trabajamos con parámetros de consulta:
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/info")
def obtener_info_cliente(user_agent: str = Header()):
return {"navegador": user_agent}
El parámetro user_agent
captura automáticamente el valor de la cabecera User-Agent
que envía el navegador. FastAPI convierte automáticamente los guiones de las cabeceras HTTP a guiones bajos en los nombres de parámetros.
Podemos hacer que una cabecera sea opcional proporcionando un valor por defecto:
@app.get("/preferencias")
def obtener_preferencias(
accept_language: str = Header(default="es-ES"),
accept_encoding: str = Header(default="gzip")
):
return {
"idioma_preferido": accept_language,
"codificacion_aceptada": accept_encoding
}
Trabajar con cabeceras personalizadas
Las aplicaciones web frecuentemente utilizan cabeceras personalizadas para transmitir información específica. Estas cabeceras suelen comenzar con X-
por convención:
@app.post("/procesar")
def procesar_datos(
x_request_id: str = Header(),
x_client_version: str = Header(default="1.0")
):
return {
"id_peticion": x_request_id,
"version_cliente": x_client_version,
"estado": "procesado"
}
Validar cabeceras con tipos
Al igual que con otros parámetros en FastAPI, podemos validar el formato de las cabeceras utilizando tipos de Python:
from typing import Optional
@app.get("/configuracion")
def obtener_configuracion(
x_max_results: int = Header(default=10),
x_timeout: Optional[float] = Header(default=None)
):
config = {"resultados_maximos": x_max_results}
if x_timeout:
config["timeout"] = x_timeout
return config
Enviar cabeceras en respuestas
Para incluir cabeceras personalizadas en nuestras respuestas, utilizamos la clase Response
de FastAPI:
from fastapi import Response
@app.get("/archivo")
def descargar_archivo(response: Response):
response.headers["Content-Type"] = "application/pdf"
response.headers["Content-Disposition"] = "attachment; filename=documento.pdf"
response.headers["X-Custom-Header"] = "valor-personalizado"
return {"mensaje": "Archivo preparado para descarga"}
También podemos establecer cabeceras directamente en el decorador de la ruta usando el parámetro response_headers
:
@app.get(
"/api/datos",
response_headers={
"X-API-Version": "2.1",
"Cache-Control": "no-cache"
}
)
def obtener_datos():
return {"datos": [1, 2, 3, 4, 5]}
Ejemplo práctico: API con cabeceras
Veamos un ejemplo que combina la lectura y envío de cabeceras en una API que gestiona información de usuarios:
from fastapi import FastAPI, Header, Response
from typing import Optional
app = FastAPI()
@app.get("/usuario/{user_id}")
def obtener_usuario(
user_id: int,
response: Response,
x_client_id: str = Header(),
accept_language: str = Header(default="es-ES")
):
# Simular datos del usuario
usuario = {
"id": user_id,
"nombre": "Juan Pérez",
"idioma": accept_language
}
# Establecer cabeceras de respuesta
response.headers["X-Request-Client"] = x_client_id
response.headers["X-User-Language"] = accept_language
response.headers["Cache-Control"] = "max-age=300"
return usuario
Este endpoint requiere la cabecera X-Client-ID
para identificar al cliente que hace la petición, mientras que Accept-Language
es opcional. En la respuesta, incluimos información sobre el cliente y configuramos el cacheo del resultado durante 5 minutos.
Las cabeceras HTTP nos proporcionan un mecanismo flexible para intercambiar metadatos entre cliente y servidor, mejorando la comunicación y permitiendo implementar funcionalidades avanzadas en nuestras APIs.
¿Te está gustando esta lección?
Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.
Más de 25.000 desarrolladores ya confían en CertiDevs
Definir modelos de respuesta con Pydantic
Los modelos de respuesta en FastAPI nos permiten estructurar y validar los datos que devolvemos desde nuestros endpoints. Utilizando Pydantic, podemos definir esquemas claros que garantizan la consistencia de nuestras respuestas y mejoran la documentación automática de la API.
Hasta ahora hemos devuelto diccionarios simples desde nuestros endpoints, pero cuando las aplicaciones crecen, necesitamos una estructura más robusta que defina exactamente qué campos contiene cada respuesta y qué tipos de datos esperamos.
Crear modelos básicos de respuesta
Un modelo de respuesta es una clase que hereda de BaseModel
de Pydantic y define la estructura de los datos que devuelve un endpoint:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class UsuarioResponse(BaseModel):
id: int
nombre: str
email: str
activo: bool
@app.get("/usuarios/{user_id}", response_model=UsuarioResponse)
def obtener_usuario(user_id: int):
# Simular datos del usuario
return {
"id": user_id,
"nombre": "Ana García",
"email": "ana@ejemplo.com",
"activo": True
}
El parámetro response_model
en el decorador le dice a FastAPI que valide y serialice la respuesta según el modelo definido. Esto garantiza que siempre devolvamos datos con la estructura correcta.
Modelos con campos opcionales
Frecuentemente necesitamos campos que pueden estar presentes o no en la respuesta. Utilizamos Optional
para definir estos campos:
from typing import Optional
from datetime import datetime
class ProductoResponse(BaseModel):
id: int
nombre: str
precio: float
descripcion: Optional[str] = None
fecha_creacion: datetime
en_stock: bool
@app.get("/productos/{producto_id}", response_model=ProductoResponse)
def obtener_producto(producto_id: int):
return {
"id": producto_id,
"nombre": "Laptop Gaming",
"precio": 1299.99,
"fecha_creacion": datetime.now(),
"en_stock": True
# descripcion se omite, será None
}
Modelos anidados
Los modelos de respuesta pueden contener otros modelos, permitiendo estructuras de datos complejas:
class DireccionResponse(BaseModel):
calle: str
ciudad: str
codigo_postal: str
class UsuarioCompletoResponse(BaseModel):
id: int
nombre: str
email: str
direccion: DireccionResponse
activo: bool
@app.get("/usuarios/{user_id}/completo", response_model=UsuarioCompletoResponse)
def obtener_usuario_completo(user_id: int):
return {
"id": user_id,
"nombre": "Carlos López",
"email": "carlos@ejemplo.com",
"direccion": {
"calle": "Calle Mayor 123",
"ciudad": "Madrid",
"codigo_postal": "28001"
},
"activo": True
}
Listas de modelos
Para endpoints que devuelven múltiples elementos, utilizamos listas de modelos:
from typing import List
@app.get("/usuarios", response_model=List[UsuarioResponse])
def listar_usuarios():
return [
{
"id": 1,
"nombre": "María Rodríguez",
"email": "maria@ejemplo.com",
"activo": True
},
{
"id": 2,
"nombre": "Pedro Martín",
"email": "pedro@ejemplo.com",
"activo": False
}
]
Personalizar la serialización
Pydantic nos permite personalizar cómo se serializan los datos utilizando el decorador field_serializer
:
from pydantic import field_serializer
class PedidoResponse(BaseModel):
id: int
total: float
fecha_pedido: datetime
estado: str
@field_serializer('total')
def serializar_total(self, valor: float) -> str:
return f"€{valor:.2f}"
@field_serializer('fecha_pedido')
def serializar_fecha(self, valor: datetime) -> str:
return valor.strftime("%d/%m/%Y %H:%M")
@app.get("/pedidos/{pedido_id}", response_model=PedidoResponse)
def obtener_pedido(pedido_id: int):
return {
"id": pedido_id,
"total": 156.75,
"fecha_pedido": datetime.now(),
"estado": "enviado"
}
Excluir campos sensibles
Los modelos de respuesta nos permiten filtrar información sensible que no debe exponerse en la API:
class UsuarioPrivadoResponse(BaseModel):
id: int
nombre: str
email: str
# Excluimos campos como password, tokens, etc.
@app.get("/perfil", response_model=UsuarioPrivadoResponse)
def obtener_perfil():
# Los datos internos pueden tener más campos
datos_completos = {
"id": 1,
"nombre": "Usuario Ejemplo",
"email": "usuario@ejemplo.com",
"password_hash": "hash_secreto", # Este campo no aparecerá en la respuesta
"token_interno": "token_secreto" # Este tampoco
}
return datos_completos # FastAPI filtra automáticamente
Respuestas con metadatos
Para APIs más sofisticadas, podemos crear modelos que incluyan metadatos adicionales junto con los datos principales:
class PaginacionResponse(BaseModel):
total: int
pagina: int
por_pagina: int
paginas_total: int
class UsuariosConPaginacionResponse(BaseModel):
usuarios: List[UsuarioResponse]
paginacion: PaginacionResponse
@app.get("/usuarios/paginados", response_model=UsuariosConPaginacionResponse)
def listar_usuarios_paginados(pagina: int = 1, limite: int = 10):
# Simular datos paginados
usuarios_simulados = [
{"id": i, "nombre": f"Usuario {i}", "email": f"user{i}@ejemplo.com", "activo": True}
for i in range(1, 6)
]
return {
"usuarios": usuarios_simulados,
"paginacion": {
"total": 50,
"pagina": pagina,
"por_pagina": limite,
"paginas_total": 5
}
}
Los modelos de respuesta con Pydantic nos proporcionan control total sobre la estructura de nuestras respuestas, garantizando consistencia, validación automática y documentación clara de nuestra API. Esta aproximación hace que nuestro código sea más mantenible y que los consumidores de la API sepan exactamente qué esperar de cada endpoint.
Aprendizajes de esta lección
- Comprender qué son las cabeceras HTTP y cómo leerlas en FastAPI.
- Aprender a enviar cabeceras personalizadas en las respuestas HTTP.
- Definir y utilizar modelos de respuesta con Pydantic para estructurar y validar datos.
- Implementar modelos con campos opcionales, anidados y listas para respuestas complejas.
- Personalizar la serialización y excluir campos sensibles en los modelos de respuesta.
Completa FastAPI y certifícate
Únete a nuestra plataforma y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.
Asistente IA
Resuelve dudas al instante
Ejercicios
Practica con proyectos reales
Certificados
Valida tus conocimientos
Más de 25.000 desarrolladores ya se han certificado con CertiDevs