Автоматическая генерация OpenAPI спецификаций в Python
Введение в OpenAPI (Swagger) в Python
Какое решение для OpenAPI является наиболее эффективным и современным?
FastAPI - современный веб-фреймворк, который из коробки генерирует полноценную OpenAPI спецификацию на основе Pydantic моделей и объявленных эндпоинтов. Это самый быстрый и простой способ получить документированное API.
Пример простого приложения:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI(title="Пример API", version="1.0.0")
class Item(BaseModel):
name: str
price: float
is_offer: bool = False
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}
@app.post("/items/", response_model=Item)
def create_item(item: Item):
return itemTelegram file python (работа с файлами telegram в python)
После запуска (uvicorn main:app --reload) эндпоинты /docs (Swagger UI) и /redoc (Redoc) содержат полную документацию. Модели Pydantic автоматически преобразуются в схемы OpenAPI.
Типичная ошибка: забыть указать response_model - тогда ответ не будет документирован. Если для одного эндпоинта нужны разные коды ответов, используется параметр responses:
@app.get("/error", responses={404: {"model": Item, "description": "Item not found"}})
def read_error():
...компас python api (api компас 3d для python)
Вопрос: Как настроить метаинформацию OpenAPI?
FastAPI позволяет задать openapi_tags, description, version и другие поля через конструктор FastAPI(). Для добавления дополнительных OpenAPI полей используется openapi_extra.
Как реализовать OpenAPI в Flask?
Для Flask существует несколько библиотек. Рассмотрим flask-smorest - одну из наиболее поддерживаемых. Она работает на основе Marshmallow схем.
from flask import Flask
from flask.views import MethodView
from flask_smorest import Api, Blueprint
from marshmallow import Schema, fields
app = Flask(__name__)
app.config["API_TITLE"] = "Flask API"
app.config["API_VERSION"] = "v1"
api = Api(app)
blp = Blueprint("items", __name__, description="Товары")
class ItemSchema(Schema):
id = fields.Int(dump_only=True)
name = fields.Str(required=True)
price = fields.Float()
@blp.route("/items")
class Items(MethodView):
@blp.response(ItemSchema(many=True))
def get(self):
return [{"id": 1, "name": "Ноутбук", "price": 1000.0}]
@blp.arguments(ItemSchema)
@blp.response(ItemSchema, code=201)
def post(self, new_item):
return new_item
api.register_blueprint(blp)
if __name__ == "__main__":
app.run()
Open api python (openapi (swagger) в python)
В результате по адресу /swagger-ui появится документация. Файл openapi.json доступен по /openapi.json.
Ошибка: при отсутствии blp.arguments или неправильном порядке декораторов документация может не сгенерироваться. Следует соблюдать порядок: arguments, затем response.
Как интегрировать OpenAPI в Django REST Framework?
Лучший выбор - drf-spectacular. Он поддерживает все возможности DRF и генерирует корректную OpenAPI 3.0 спецификацию.
# settings.py
INSTALLED_APPS = [
...
'drf_spectacular',
]
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}
SPECTACULAR_SETTINGS = {
'TITLE': 'Django API',
'VERSION': '1.0.0',
}проверка ключа python (проверка api ключа в python)
# views.py
from rest_framework import viewsets
from .models import Item
from .serializers import ItemSerializer
from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiExample
@extend_schema(tags=['items'])
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all()
serializer_class = ItemSerializer
@extend_schema(
summary="Получить список товаров",
parameters=[
OpenApiParameter(name='search', type=str),
]
)
def list(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)Добавив path('api/schema/', SpectacularAPIView.as_view(), name='schema') в urls, получаем /api/schema/. Swagger UI подключается через SpectacularSwaggerView.
Проблема: для вьюх, не наследующих от стандартных представлений DRF, нужно вручную аннотировать параметры с помощью @extend_schema. Иначе документация может быть неполной.
Как создать OpenAPI спецификацию вручную с помощью apispec?
Если требуется полный контроль, используется библиотека apispec. Она позволяет описать пути и схемы вручную.
from apispec import APISpec
from apispec.ext.marshmallow import MarshmallowPlugin
from marshmallow import Schema, fields
spec = APISpec(
title="Manual API",
version="1.0.0",
openapi_version="3.0.2",
plugins=[MarshmallowPlugin()],
)
class Item(Schema):
id = fields.Int()
name = fields.Str()
spec.components.schema("Item", schema=Item)
spec.path(
path="/items",
operations=dict(
get=dict(
summary="Get items",
responses={"200": {"content": {"application/json": {"schema": "Item"}}}}
)
)
)
# Сохранение в файл
import json
with open("openapi.json", "w") as f:
json.dump(spec.to_dict(), f, indent=2)Ошибка: несоответствие структуры OpenAPI. Рекомендуется сверить сгенерированный JSON с помощью валидатора (например, prance).
Расширенные примеры работы с OpenAPI в Python
Кастомизация метаданных и группировка эндпоинтов в FastAPI
from fastapi import FastAPI, APIRouter
from pydantic import BaseModel
app = FastAPI(
title="Сложное API",
version="2.0.0",
description="API с дополнительными метаданными",
openapi_tags=[
{"name": "users", "description": "Операции с пользователями"},
{"name": "items", "description": "Управление товарами"},
]
)
router_items = APIRouter(tags=["items"])
class ItemCreate(BaseModel):
name: str
price: float
@router_items.post("/items", response_model=ItemCreate, summary="Создать товар")
def create_item(item: ItemCreate):
return item
app.include_router(router_items)
# Добавление произвольного поля в OpenAPI
app.openapi_extra = {
"x-logo": {
"url": "https://example.com/logo.png"
}
}Результат: в /openapi.json появится секция x-logo, а теги объединят эндпоинты. Swagger UI отобразит группы.
Добавление схемы безопасности (Bearer Token) в FastAPI
from fastapi.security import HTTPBearer
from fastapi import FastAPI, Depends
app = FastAPI()
security = HTTPBearer()
@app.get("/secure", dependencies=[Depends(security)])
def secure_endpoint():
return {"message": "Доступ разрешён"}
# Автоматически в OpenAPI добавится компонент securitySchemes с bearerAuth// Фрагмент openapi.json
"components": {
"securitySchemes": {
"HTTPBearer": {
"type": "http",
"scheme": "bearer"
}
}
},
"security": [{"HTTPBearer": []}]Генерация клиентского кода с помощью openapi-generator
После получения openapi.json можно сгенерировать клиент на любом языке.
# Установка генератора
npm install @openapitools/openapi-generator-cli -g
# Генерация Python клиента
openapi-generator-cli generate -i openapi.json -g python -o ./python-client
# Генерация TypeScript клиента
openapi-generator-cli generate -i openapi.json -g typescript-axios -o ./ts-clientВ полученном клиенте все эндпоинты и модели уже типизированы. Это ускоряет разработку и снижает количество ошибок.
Тестирование API с использованием OpenAPI схемы
Можно проверять ответы на соответствие схеме с помощью библиотеки jsonschema.
import json
import requests
from jsonschema import validate, ValidationError
# Загрузка схемы
with open("openapi.json") as f:
spec = json.load(f)
# Получение схемы для ответа 200 для метода GET /items
schema = spec["paths"]["/items"]["get"]["responses"]["200"]["content"]["application/json"]["schema"]
# Запрос к API
response = requests.get("http://localhost:8000/items")
try:
validate(instance=response.json(), schema=schema)
print("Ответ соответствует OpenAPI схеме")
except ValidationError as e:
print(f"Ошибка валидации: {e.message}")Генерация OpenAPI схемы из существующих данных (на примере Flask + apispec)
from flask import Flask, jsonify
from apispec import APISpec
from apispec.ext.flask import FlaskPlugin
from apispec_webframeworks.flask import FlaskPlugin as FlaskPlugin2 # альтернатива
app = Flask(__name__)
@app.route("/api/data")
def get_data():
return jsonify([{"id": 1, "value": "test"}])
spec = APISpec(title="Dynamic API", version="1.0.0", openapi_version="3.0.2", plugins=[FlaskPlugin2()])
with app.test_request_context():
spec.path(view=get_data)
import json
print(json.dumps(spec.to_dict(), indent=2))Результат: будет сгенерирован путь /api/data с ответом 200, но без указания модели (так как нет схемы). Для полноты нужно добавить схему Marshmallow.
Работа с несколькими версиями API
FastAPI позволяет иметь несколько приложений с разными префиксами.
from fastapi import FastAPI
app_v1 = FastAPI(title="API v1", version="1.0.0")
app_v2 = FastAPI(title="API v2", version="2.0.0")
@app_v1.get("/status")
def status_v1():
return {"version": "v1"}
@app_v2.get("/status")
def status_v2():
return {"version": "v2"}
# Монтируем с разными префиксами
from fastapi import APIRouter
router = APIRouter()
router.mount("/v1", app_v1)
router.mount("/v2", app_v2)
# Итоговое приложение
app = FastAPI()
app.include_router(router)
# Документация каждой версии доступна по /v1/docs и /v2/docs