Главное, что нужно знать о форматах ответов API и JSON: если ваш API не возвращает данные в формате JSON — это повод насторожиться.
Почти все современные API используют именно JSON — он лёгкий, удобный, человекочитаемый и поддерживается практически всеми языками программирования.
FastAPI из коробки работает с JSON, так что нам почти не придётся прилагать усилия для настройки этого процесса.
Давайте создадим небольшой пример. Сначала настроим эндпоинт, который возвращает простой ответ:
from fastapi import FastAPI
app = FastAPI()
@app.get("/hello")
async def say_hello():
return {"message": "Hello, FastAPI!"}
Если вы запустите приложение через Uvicorn (uvicorn main:app --reload), перейдите на http://127.0.0.1:8000/hello и увидите результат, FastAPI автоматически вернёт JSON:
{
"message": "Hello, FastAPI!"
}
FastAPI автоматически конвертирует Python-объекты (словари, списки) в JSON. Всё, что нужно делать — просто возвращать данные из обработчика (функции) эндпоинта.
Структура JSON-ответов
JSON-ответы представляют собой структурированные данные в виде объектов, массивов, строк и чисел. Вот пример более сложного ответа:
@app.get("/user")
async def get_user():
return {
"id": 1,
"name": "John Doe",
"email": "johndoe@example.com",
"active": True,
"roles": ["admin", "editor"]
}
Здесь возвращается сложный объект со вложенными структурами данных: строками, булевыми значениями и списками.
Результат запроса:
{
"id": 1,
"name": "John Doe",
"email": "johndoe@example.com",
"active": true,
"roles": ["admin", "editor"]
}
Настройка обработки ответов
FastAPI позволяет настраивать ответы для более сложных случаев, например, если нужно вернуть объект определённого типа, изменить заголовки или статус ответа.
Возвращаемые типы
FastAPI поддерживает аннотацию типов, что делает наш код более читаемым и понятным. В простых случаях можно использовать Python-словари, списки или строки.
from typing import Dict
@app.get("/product", response_model=Dict[str, str])
async def get_product():
return {"name": "Laptop", "brand": "FastAPI"}
FastAPI проверит, что возвращаемый объект соответствует указанной структуре. Если нет — выбросит ошибку.
Установка HTTP-статусов
Иногда нам нужно вернуть не только данные, но и особый HTTP-статус. Например, 201 Created для успешного создания ресурса. Это можно сделать с помощью класса JSONResponse:
from fastapi.responses import JSONResponse
@app.post("/item")
async def create_item():
return JSONResponse(status_code=201, content={"message": "Item created"})
Теперь, вызывая этот эндпоинт, вы получите:
{
"message": "Item created"
}
с HTTP-статусом 201.
Установка заголовков ответа
Заголовки headers используются для передачи дополнительных данных о запросе или ответе. Например, вы можете указать тип контента ответа:
from fastapi.responses import JSONResponse
@app.get("/custom-header")
async def custom_header():
return JSONResponse(
content={"message": "Custom Header!"},
headers={"X-Custom-Header": "Awesome FastAPI"}
)
Результат запроса:
HTTP/1.1 200 OK
Content-Type: application/json
X-Custom-Header: Awesome FastAPI
{
"message": "Custom Header!"
}
Использование response_model для строгой типизации
FastAPI предлагает мощный инструмент для описания структуры возвращаемого JSON — response_model. Он помогает нам гарантировать, что возвращаемый ответ всегда следует заданной схеме.
Пример с использованием Pydantic
Создадим Pydantic-модель для описания структуры ответа:
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str
email: str
active: bool
roles: list[str]
@app.get("/user", response_model=User)
async def get_user():
return {
"id": 1,
"name": "John Doe",
"email": "johndoe@example.com",
"active": True,
"roles": ["admin", "editor"]
}
response_model позволяет FastAPI автоматически проверить соответствие ответа модели User. Если вернётся объект, не соответствующий модели, вы получите ошибку.
Обработка необязательных полей
Не все данные в JSON обязаны быть заполнены. FastAPI и Pydantic позволяют описывать опциональные поля.
from typing import Optional
class Product(BaseModel):
name: str
description: Optional[str] = None # Поле может быть пустым
@app.get("/product", response_model=Product)
async def get_product():
return {"name": "Laptop"} # "description" не передаётся
Результат запроса:
{
"name": "Laptop",
"description": null
}
Кастомизация форматов ответа
Иногда JSON — это круто, но хочется большего. Например, вернуть CSV или XML. FastAPI позволяет кастомизировать формат ответа с помощью Response.
Приведём пример с текстовым ответом
from fastapi.responses import Response
@app.get("/text")
async def get_text():
return Response(content="Hello, this is plain text!", media_type="text/plain")
Результат:
Hello, this is plain text!
А теперь – пример с кастомным форматом:
@app.get("/custom-response")
async def custom_response():
custom_json = '<custom><message>Hello, XML!</message></custom>'
return Response(content=custom_json, media_type="application/xml")
Это полезно, если ваш клиент ожидает данные в нестандартном формате.
Примеры сложных ответов
Возврат списков объектов
Если ваш API должен вернуть несколько объектов, просто верните список словарей или Pydantic-моделей:
class Item(BaseModel):
id: int
name: str
price: float
@app.get("/items", response_model=list[Item])
async def get_items():
return [
{"id": 1, "name": "Laptop", "price": 999.99},
{"id": 2, "name": "Phone", "price": 699.99}
]
Результат:
[
{"id": 1, "name": "Laptop", "price": 999.99},
{"id": 2, "name": "Phone", "price": 699.99}
]
Обработка ошибок в ответах
FastAPI позволяет легко формировать ответы об ошибках. Вы можете использовать HTTPException для возврата кастомных сообщений:
from fastapi import HTTPException
@app.get("/error")
async def error():
raise HTTPException(status_code=404, detail="Item not found")
Ответ:
{
"detail": "Item not found"
}
Теперь вы знаете, как работать с ответами в FastAPI, использовать JSON по максимуму, настраивать заголовки и даже возвращать пользовательские форматы. Далее нас ждёт встроенная документация в FastAPI — это то, что заставляет разработчиков любить этот фреймворк ещё сильнее.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ