fastapi 的 middleware 怎么改成同步模式?

作者站长头像
站长
· 阅读数 6

我想写一个验证身份的中间件

@app.middleware("http")
async def get_user_id(request: Request, call_next):
    from core.mysql.models import SessionTable
    from core.mysql.models import SessionTable
    from core.mysql.models import UserTable
    from apps.user.schemas import User
    from playhouse.shortcuts import model_to_dict

    session_key = request.cookies.get("session_key")
    if session_key:
        try:
            session: SessionTable | None = SessionTable.get_or_none(
                session_key=session_key)
            if session:
                user_id = session.user_id
                if user_id:
                    request.state.user_id = user_id
                    user: UserTable | None = UserTable.get_or_none(id=user_id)
                    request.state.user = User(**model_to_dict(user))
        except Exception as error:
            logger.warning(error)
    if not session_key or not request.state.get('user_id', None):
        return Response(status_code=401, content='Invalid token')
    response = call_next(request)
    return response

但是有一个问题,就是中间件的函数,必须要加 async,这样就变成了一个同步函数了

非常的令人讨厌!

我不喜欢异步,因为我用的 peewee 等库都不支持异步

比如我们需要在 middleware 中使用 pymysql、redis-py 这些同步库的时候,就会有麻烦

怎么让中间件不加 async?


如果我把 def get_user_id 前面的 async 删掉的话,就会报错

ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/Users/ponponon/.local/share/virtualenvs/whoisface-dx42pPyK/lib/python3.11/site-packages/uvicorn/protocols/http/h11_impl.py", line 429, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ponponon/.local/share/virtualenvs/whoisface-dx42pPyK/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__
    return await self.app(scope, receive, send)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ponponon/.local/share/virtualenvs/whoisface-dx42pPyK/lib/python3.11/site-packages/fastapi/applications.py", line 276, in __call__
    await super().__call__(scope, receive, send)
  File "/Users/ponponon/.local/share/virtualenvs/whoisface-dx42pPyK/lib/python3.11/site-packages/starlette/applications.py", line 122, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/Users/ponponon/.local/share/virtualenvs/whoisface-dx42pPyK/lib/python3.11/site-packages/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "/Users/ponponon/.local/share/virtualenvs/whoisface-dx42pPyK/lib/python3.11/site-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "/Users/ponponon/.local/share/virtualenvs/whoisface-dx42pPyK/lib/python3.11/site-packages/starlette/middleware/base.py", line 108, in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: object Response can't be used in 'await' expression
回复
1个回答
avatar
test
2024-07-01
from fastapi import FastAPI, Request, Response
from starlette.concurrency import run_in_threadpool

app = FastAPI()

def sync_code():
    # 这里放你的同步代码
    pass

@app.middleware("http")
async def sync_middleware(request: Request, call_next):
    await run_in_threadpool(sync_code)
    response = await call_next(request)
    return response

你删掉了async,自然就会报错,因为你下面的代码用了await,中间件是要异步的,你试试上面的方法

回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容