Система контроллеров, декораторы маршрутизации, параметры путей, группы контроллеров, именованные маршруты
Декларативное описание API с помощью контроллеров и декораторов маршрутизации
В Starlite маршрутизация построена вокруг двух ключевых концепций:
Starlite предоставляет декораторы для всех стандартных HTTP-методов:
from starlite import get, post, put, delete, patch
@get("/items")
def list_items() -> list[str]:
return ["item1", "item2"]
@post("/items")
def create_item(data: ItemCreate) -> Item:
...
@put("/items/{item_id:int}")
def update_item(item_id: int, data: ItemUpdate) -> Item:
...
@delete("/items/{item_id:int}")
def delete_item(item_id: int) -> None:
...
@patch("/items/{item_id:int}")
def partial_update_item(item_id: int, data: ItemUpdate) -> Item:
...Декораторы принимают множество параметров для настройки поведения:
@get(
"/users",
status_code=200,
tags=["users"],
summary="List all users",
description="Returns a paginated list of users",
operation_id="listUsers",
deprecated=False,
)
def list_users() -> list[User]:
...| Параметр | Тип | Описание |
|---|---|---|
status_code | int | HTTP-статус ответа по умолчанию |
tags | list[str] | Теги для группировки в OpenAPI |
summary | str | Краткое описание для документации |
description | str | Подробное описание для документации |
operation_id | str | Уникальный ID операции для OpenAPI |
deprecated | bool | Пометить эндпоинт как устаревший |
Контроллер — это класс, наследующийся от Controller:
from starlite import Controller, get, post
class UserController(Controller):
path = "/users"
@get()
def list_users(self) -> list[User]:
return [...]
@post()
def create_user(self, data: UserCreate) -> User:
...Контроллер поддерживает несколько атрибутов для настройки:
class ItemController(Controller):
path = "/items"
dependencies = {"repo": Provide(ItemRepository)}
guards = [AuthGuard()]
middleware = [LoggingMiddleware()]
exception_handlers = {
ItemNotFoundError: handle_item_not_found
}
@get()
def list_items(self, repo: ItemRepository) -> list[Item]:
return repo.find_all()| Атрибут | Описание |
|---|---|
path | Базовый путь для всех маршрутов контроллера |
dependencies | DI-зависимости, доступные всем хендлерам |
guards | Охранники доступа (аналог middleware) |
middleware | Middleware для контроллера |
exception_handlers | Обработчики исключений |
Контроллеры поддерживают наследование для переиспользования логики:
class BaseController(Controller):
guards = [BaseAuthGuard()]
class UserController(BaseController):
path = "/users"
@get()
def list_users(self) -> list[User]:
...Параметры пути объявляются в фигурных скобках:
@get("/users/{user_id:int}")
def get_user(self, user_id: int) -> User:
...
@get("/files/{file_path:path}")
def get_file(self, file_path: str) -> bytes:
...Поддерживаемые типы:
int — целые числаfloat — числа с плавающей точкойstr — строки (по умолчанию)path — весь оставшийся путь (включая /)uuid — UUIDПараметры запроса извлекаются автоматически по имени аргумента:
@get("/search")
def search(
self,
q: str,
limit: int = 10,
offset: int = 0,
sort_by: str = "created_at",
) -> list[Item]:
...Запрос: GET /search?q=python&limit=20&offset=10
Используйте Query для настройки валидации:
from starlite import Query
@get("/items")
def list_items(
self,
page: int = Query(ge=1, le=1000, default=1),
page_size: int = Query(ge=1, le=100, default=20),
) -> list[Item]:
...Параметры Query:
ge — больше или равноle — меньше или равноgt — строго большеlt — строго меньшеdefault — значение по умолчаниюrequired — обязателен ли параметрЗаголовки извлекаются через Header:
from starlite import Header
@get("/protected")
def protected_route(
self,
x_api_key: str = Header(header="X-API-Key"),
) -> dict:
...Cookies извлекаются через Cookie:
from starlite import Cookie
@get("/dashboard")
def dashboard(
self,
session_id: str = Cookie(default=""),
) -> dict:
...Для группировки контроллеров и хендлеров используется Router:
from starlite import Router
from .users import UserController
from .items import ItemController
from .health import health_handler
api_router = Router(
path="/api/v1",
route_handlers=[UserController, ItemController, health_handler],
)
app = Starlite(route_handlers=[api_router])Роутеры можно вкладывать друг в друга:
# routers/api_v1.py
api_v1_router = Router(
path="/api/v1",
route_handlers=[...],
)
# routers/api_v2.py
api_v2_router = Router(
path="/api/v2",
route_handlers=[...],
)
# main.py
app = Starlite(
route_handlers=[api_v1_router, api_v2_router]
)Для генерации URL используйте operation_id:
@get("/users/{user_id:int}", operation_id="getUserById")
def get_user(self, user_id: int) -> User:
...В OpenAPI-схеме этот ID будет использоваться для идентификации операции.
Starlite не имеет встроенной функции url_for, но operation_id используется для генерации ссылок в документации.
Starlite автоматически сериализует возвращаемые значения:
@get("/text")
def text_response() -> str:
return "Plain text"
@get("/json")
def json_response() -> dict:
return {"key": "value"}
@get("/html")
def html_response() -> str:
return "<h1>Hello</h1>"Для тонкого контроля используйте Response:
from starlite import Response
@get("/custom")
def custom_response() -> Response[dict]:
return Response(
content={"key": "value"},
status_code=201,
headers={"X-Custom": "header"},
media_type="application/json",
)Для отправки файлов:
from starlite import Response
@get("/download/{file_id:str}")
def download_file(file_id: str) -> Response[bytes]:
file_content = read_file(file_id)
return Response(
content=file_content,
media_type="application/octet-stream",
headers={"Content-Disposition": f"attachment; filename={file_id}"},
)Для редиректов:
from starlite import Redirect
@get("/old-path")
def redirect_old_path() -> Redirect:
return Redirect(path="/new-path")
@get("/permanent-redirect")
def permanent_redirect() -> Redirect:
return Redirect(path="/new-path", status_code=301)Для потоковой передачи данных:
from starlite import stream
@get("/stream")
@stream()
async def stream_data(self) -> list[str]:
for i in range(10):
yield f"data: {i}\n"# ✅ Хорошо
class UserController(Controller):
path = "/users"
@get()
def list_users(self): ...
@post()
def create_user(self): ...
# ❌ Плохо — разрозненные хендлеры
@get("/users")
def list_users(): ...
@post("/users")
def create_user(): ...# ✅ Хорошо
@get("/users/{user_id:int}")
def get_user(self, user_id: int) -> User:
...
# ❌ Плохо — нет аннотации
@get("/users/{user_id:int}")
def get_user(self, user_id: int):
...@get(
"/users/{user_id:int}",
operation_id="getUserById",
summary="Get user by ID",
)
def get_user(self, user_id: int) -> User:
...# routers/__init__.py
api_router = Router(path="/api", route_handlers=[...])
auth_router = Router(path="/auth", route_handlers=[...])
# main.py
app = Starlite(route_handlers=[api_router, auth_router])Система роутинга Starlite предлагает:
В следующей теме мы изучим систему Dependency Injection.
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.