Skip to content

从 Flask / FastAPI 迁移到 Robyn ​

Robyn 的路由写法看起来很像 Flask,注入方式又有点像 FastAPI,很多人因此以为迁移只是改几个装饰器的事。真动起来会发现:真正要改的是读取请求参数的习惯,以及那些基于 ASGI 生态的三方库。下面按迁移顺序拆解。

一、先认清一个前提:Robyn 不是 ASGI 框架 ​

这是迁移前必须接受的事实。Flask/FastAPI(Starlette)都跑在 ASGI 或 WSGI 之上,因而能用大量通用中间件;Robyn 的请求生命周期由 Rust 层处理,Python 只负责业务处理器,所以:

原本依赖的东西迁移后
ASGI 中间件(Starlette 生态)❌ 不可用,需改用 before_request / after_request
Sentry、OpenTelemetry 的 ASGI Instrumentor❌ 需用专门的 Robyn 插桩,或自己包一层中间件
依赖并关系型的三方扩展(如 fastapi-users)❌ 基本无法复用
Pydantic 模型做校验✅ 仍可在处理器里手动调用
Jinja2 模板✅ 官方支持,见 模板
SQLAlchemy / asyncpg 等纯 Python 库✅ 与框架无关,直接迁移

判断要不要迁移的经验法则:如果你的项目大量使用了 FastAPI 的 Depends、中间件栈和生态扩展,迁移成本会远高于性能收益;如果项目是薄薄的 REST 层 + 自己的业务逻辑,迁移通常只需要一两天。

二、路由与路径参数 ​

语法对照:

场景Flask / FastAPIRobyn
声明@app.get("/users")@app.get("/users")
路径参数/users/<int:id>、/users/{id}/users/:id
读取方式函数形参 idrequest.params["id"] 或按类型注入的 path_params
蓝图Blueprint(prefix="/api")SubRouter

Flask 写法 → Robyn 写法:

python
from robyn import Robyn

app = Robyn(__file__)

@app.get("/users/:id")
async def get_user(request):
    user_id = request.params["id"]        # 路径参数,永远是字符串
    page = request.query_params.get("page")   # 查询参数
    return {"id": user_id, "page": page}

也可以让 Robyn 按类型注解把各部分注入进来,省掉一层 request. 前缀:

python
from robyn import Request, QueryParams
from robyn.types import PathParams

@app.get("/users/:id")
async def get_user(request: Request, path_params: PathParams, query_params: QueryParams):
    return {"id": path_params["id"], "page": query_params.get("page")}

注意 Robyn 的路径参数没有类型转换器::id 抓到的永远是字符串,int(id) 要自己做。

三、请求体与响应 ​

请求体解析统一走 request.json()(非 JSON 会抛 ValueError):

python
@app.post("/orders")
async def create_order(request):
    data = request.json()
    order_id = create_order_in_db(data)
    return {"order_id": order_id}

返回值会自动转换:字符串 → text/plain,dict/list → JSON。需要自定义状态码时用 Response:

python
from robyn import Response

@app.post("/orders")
async def create_order(request):
    data = request.json()
    return Response(
        status_code=201,
        body='{"status": "created"}',
        headers={"Content-Type": "application/json"},
    )

请求对象的完整字段见 请求对象,各种返回形式见 响应对象。

四、中间件:Flask/FastAPI 的那一套要改写 ​

@app.before_request 接收并返回请求对象;一旦返回 Response,后续中间件和处理器都会被跳过(但 after_request 仍会执行):

python
from robyn import Request, Response

@app.before_request("/api/*")
async def require_token(request: Request):
    if not request.headers.get("authorization"):
        return Response(status_code=401, body="unauthorized")
    request.headers.set("x-request-id", gen_id())
    return request

@app.after_request("/api/*")
def add_server_header(response: Response):
    response.headers.set("x-powered-by", "robyn")
    return response

全局异常处理替换 FastAPI 的 @app.exception_handler:

python
from robyn import Response

@app.exception
def handle_exception(error):
    return Response(status_code=500, body=f"error msg: {error}")

五、依赖注入与校验 ​

FastAPI 的 Depends 在 Robyn 里没有等价物,官方提供了轻量的依赖注入能力(见 依赖注入),更常见的做法是自己<|hy_place▁holder▁no▁813|>一层工厂函数:

python
from functools import wraps

def with_db(func):
    @wraps(func)
    async def wrapper(request, *args, **kwargs):
        kwargs["conn"] = await pool.acquire()
        try:
            return await func(request, *args, **kwargs)
        finally:
            await pool.release(kwargs["conn"])
    return wrapper

@app.get("/users/:id")
@with_db
async def get_user(request, conn, **kwargs):
    return await fetch_user(conn, request.params["id"])

入参校验建议用 Pydantic 手动包一层,官方文档里也有 Pydantic 集成 的示例。

六、迁移清单 ​

  1. 把每个 <var> / {var} 改成 :var,参数取出方式改成 request.params
  2. 逐条替换 before_request / after_request
  3. 移走所有依赖 ASGI 的中间件与工具,逐个找替代
  4. 确认 sync / async 都支持——Robyn 两者都接受,不必一次性全改成协程
  5. 用 --dev 开发,用 --processes 上生产(详见 生产部署清单)
  6. 别忘了多进程下不共享内存,模块级可变状态需要外置到 Redis 等存储(见 多进程执行)

小结 ​

迁移的收益在 CPU 密集度较低、请求量大的 JSON API 上最明显;而成本几乎全部集中在"替换 ASGI 生态依赖"这一项。如果你只是想要一个更快的 FastAPI,先确认自己用到的扩展有没有 Robyn 版本,比直接动手改代码更重要。

基于 MIT 许可发布 · 隐私政策