Async
Async-хендлеры и async middleware в Fusion Python
Обзор
Fusion поддерживает async HTTP-хендлеры и async middleware. Sync и async могут сосуществовать в одном приложении. Если любой middleware или сам хендлер async, этот запрос выполняется на общем asyncio runtime, связанном из Rust.
Async-хендлеры
Определите метод через async def:
from fusion_framework.api import FusionBaseApi
from fusion_framework.route import route
from fusion_framework import status
@route("api/[module]/")
class JobModule(FusionBaseApi):
def get(self):
return self.response({"mode": "sync"}, status=status.HTTP_SUCCESS)
async def post(self):
# await DB / HTTP / asyncio APIs here
result = await do_work()
return self.response(result, status=status.HTTP_201_CREATED)Правила:
- Та же привязка параметров, что у sync (
path/body/query) - Те же
self.response(...)/HTTPException - Не блокируйте event loop тяжёлым sync I/O внутри
async def— используйте async-библиотеки илиasyncio.to_thread
Async middleware
async def auth_middleware(request, call_next):
token = (request.get("headers") or {}).get("authorization")
if not token:
return {"status": 401, "body": {"detail": "missing token"}}
user = await load_user(token) # your async call
if not user:
return {"status": 401, "body": {"detail": "invalid"}}
request.setdefault("state", {})
request["state"]["user"] = user
return await call_next(request)Регистрируйте как любой другой middleware:
MIDDLEWARE = [auth_middleware]Важно: в async middleware всегда:
return await call_next(request)Когда цепочка async, call_next возвращает awaitable.
Смешивание sync и async
def logging_mw(request, call_next):
print(request["path"])
return call_next(request)
async def timing_mw(request, call_next):
import time
t0 = time.perf_counter()
response = await call_next(request)
ms = (time.perf_counter() - t0) * 1000
print(f"{request['path']} {ms:.1f}ms")
return response
MIDDLEWARE = [logging_mw, timing_mw]Если любой middleware в цепочке (или хендлер) — coroutine function, Fusion выполняет всю цепочку через await.
Полный пример
# main.py
import src.modules.jobs.jobs
from fusion_framework.app import FusionApp
from fusion_framework.config import get_settings, load_settings_module
async def attach_request_id(request, call_next):
import uuid
request.setdefault("state", {})
request["state"]["request_id"] = str(uuid.uuid4())
return await call_next(request)
MIDDLEWARE = [attach_request_id]
def main() -> None:
load_settings_module("settings")
app = FusionApp(get_settings())
for mw in MIDDLEWARE:
app.use(mw)
app.listen()
if __name__ == "__main__":
main()# src/modules/jobs/jobs.py
from fusion_framework.api import FusionBaseApi
from fusion_framework.route import route
from fusion_framework import status
@route("api/[module]/")
class JobModule(FusionBaseApi):
async def get(self):
rid = self.state.get("request_id")
data = await fetch_jobs()
return self.response(
{"request_id": rid, "jobs": data},
status=status.HTTP_SUCCESS,
)Под капотом
- Rust принимает HTTP-запрос и диспатчит в Python bridge хендлера
- Выполняется цепочка middleware (sync- или async-путь)
- Async-работа отправляется в общий asyncio loop Fusion, чтобы параллельные запросы не блокировали друг друга
Event loop в main.py сами не запускаете — жизненным циклом сервера владеет app.listen().
Чеклист
| Цель | Паттерн |
|---|---|
| Async API-метод | async def get/post/... |
| Async middleware | async def mw(request, call_next): return await call_next(request) |
| Регистрация глобального MW | MIDDLEWARE + app.use в main.py |
| Чтение данных MW в хендлере | self.state["…"] |
| Ошибки | raise HTTPException(...) (работает и в async-хендлерах) |
См. также Middleware и Router.