FastAPI Modelo de Respuesta
El modelo de respuesta se utiliza para declarar la estructura de datos devuelta por la API. Al declarar el modelo de respuesta, FastAPI realiza automáticamente la validación, serialización y filtrado de los datos de salida, asegurando que el cliente solo reciba los datos esperados.
Usar anotaciones de tipo de retorno
La forma más sencilla es anotar directamente el tipo de retorno de la función de operación de ruta:
Ejemplo
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: int):
# Los datos devueltos se filtran y validan automáticamente según el modelo Item
return {
"name": "Foo",
"description": "A very nice Item",
"price": 22.2,
"secret": "this should not be visible", # Los campos que no están en Item serán filtrados
}
FastAPI utiliza el modelo de respuesta para realizar las siguientes tareas:
| Función | Descripción |
|---|---|
| Validación de datos | Si los datos devueltos no coinciden con el modelo, significa que hay un problema en el código de la aplicación. |
| Filtrado de datos | Solo devuelve los campos definidos en el modelo y filtra los campos sobrantes (esto es importante para la seguridad). |
| Generación de documentación | Mostrar la estructura de respuesta en la documentación de OpenAPI. |
| Serialización | Utiliza Pydantic (implementado en Rust) para convertir datos a JSON de forma eficiente. |
Parámetro response_model
Cuando el tipo de retorno no coincide con el modelo de respuesta, usa elresponse_modelparámetro del decorador:
Ejemplo
from pydantic import BaseModel, EmailStr
app = FastAPI()
# Modelo de entrada (incluye contraseña)
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
# Modelo de salida (no incluye contraseña)
class UserOut(BaseModel):
username: str
email: EmailStr
# La función recibe UserIn, pero la respuesta usa UserOut
@app.post("/users/", response_model=UserOut)
async def create_user(user: UserIn):
# El user devuelto incluye la contraseña, pero response_model=UserOut la filtrará
return user
Esta es una medida importante para proteger datos sensibles. Usar diferentes modelos de entrada y salida garantiza que campos sensibles como contraseñas nunca aparezcan en la respuesta de la API.
Tipo de retorno y filtrado de datos
Se recomienda usar herencia de clases para separar los modelos de entrada/salida. De esta manera, tanto el editor como mypy pueden entender correctamente los tipos, y FastAPI puede filtrar los datos.
Ejemplo
from pydantic import BaseModel, EmailStr
app = FastAPI()
# Modelo de usuario base
class BaseUser(BaseModel):
username: str
email: EmailStr
# Modelo de entrada: hereda del modelo base y agrega el campo de contraseña
class UserIn(BaseUser):
password: str
@app.post("/users/")
async def create_user(user: UserIn) -> BaseUser:
# El tipo de retorno se anota como BaseUser; FastAPI filtrará el campo password
# El editor y mypy también están satisfechos porque UserIn es una subclase de BaseUser
return user
Excluir valores predeterminados no establecidos
Usandoresponse_model_exclude_unset=True, solo se devuelven los campos cuyo valor se ha establecido realmente:
Ejemplo
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float = 10.5
tags: list[str] = []
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}
@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
return items[item_id]
Comparación de respuestas para diferentes item_id:
| item_id | Respuesta | Descripción |
|---|---|---|
foo | {"name": "Foo", "price": 50.2} | Solo devuelve los campos establecidos explícitamente, omitiendo los valores predeterminados. |
bar | {"name": "Bar", "description": "...", "price": 62, "tax": 20.2} | Todos los campos tienen valores reales, se devuelven todos. |
Parámetros de filtrado relacionados:
| Parámetro | Descripción |
|---|---|
response_model_exclude_unset | Excluir campos sin valor establecido |
response_model_exclude_defaults | Excluir campos con valor predeterminado |
response_model_exclude_none | Excluir campos con valor None |
Incluir/Excluir campos específicos
Usaresponse_model_includeyresponse_model_excludepara controlar con precisión los campos de salida:
Ejemplo
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float = 10.5
# Solo incluir name y description
@app.get("/items/{item_id}", response_model=Item, response_model_include={"name", "description"})
async def read_item_name(item_id: str):
return items[item_id]
# Excluir tax
@app.get("/items/{item_id}/no-tax", response_model=Item, response_model_exclude={"tax"})
async def read_item_no_tax(item_id: str):
return items[item_id]
Aunque
response_model_includeyresponse_model_excludepueden filtrar campos rápidamente, se recomienda usar múltiples clases de modelos en su lugar, porque no pueden eliminar los campos excluidos de la documentación de OpenAPI.
Resumen
- Usa
response_modelel parámetro o las anotaciones de tipo de retorno para declarar el modelo de respuesta. - La función principal del modelo de respuesta es el filtrado de datos, protegiendo los datos sensibles de ser expuestos.
- Usa diferentes modelos para entrada y salida, asegurando que campos como contraseñas no aparezcan en la respuesta.
response_model_exclude_unsetSolo devuelve los campos realmente establecidos.- Se recomienda usar herencia de modelos + anotaciones de tipo de retorno, en lugar de
response_model_include/exclude