You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FastAPI登录注册API在AWS API Gateway触发内部服务器错误求助

问题描述

登录与注册API在本地环境(http://127.0.0.1:8000/login)可正常响应,但部署到AWS Lambda+API Gateway后抛出异常;其他API均正常工作,仅登录注册接口出问题。移除try-except块会触发内部服务器错误,已配置CORS规则。

登录API参考代码:

@route.post("/login", tags=['Account'])
def login(form_data: OAuth2PasswordRequestForm = Depends()):
    try:
        user = db.query(UserModel).filter_by(email=form_data.username).first()
        if user and verify_password(form_data.password, user.password):
            return {"access_token": signJWT(user), "token_type": "Bearer"}
        else:
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password")
    except:
        db.rollback()
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED) 

CloudWatch中捕获的错误日志:

Traceback (most recent call last):
  File "/var/task/mangum/protocols/http.py", line 58, in run
    await app(self.scope, self.receive, self.send)
  File "/var/task/fastapi/applications.py", line 276, in __call__
    await super().__call__(scope, receive, send)
  File "/var/task/starlette/applications.py", line 122, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/var/task/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "/var/task/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "/var/task/starlette/middleware/cors.py", line 83, in __call__
    await self.app(scope, receive, send)
  File "/var/task/starlette/middleware/exceptions.py", line 79, in __call__
    raise exc
  File "/var/task/starlette/middleware/exceptions.py", line 68, in __call__
    await self.app(scope, receive, sender)
  File "/var/task/fastapi/middleware/asyncexitstack.py", line 21, in __call__
    raise e
  File "/var/task/fastapi/middleware/asyncexitstack.py", line 18, in __call__
    await self.app(scope, receive, send)
  File "/var/task/starlette/routing.py", line 718, in __call__
    await route.handle(scope, receive, send)
  File "/var/task/starlette/routing.py", line 276, in handle
    await self.app(scope, receive, send)
  File "/var/task/starlette/routing.py", line 66, in app
    response = await func(request)
  File "/var/task/fastapi/routing.py", line 237, in app
    raw_response = await run_endpoint_function(
  File "/var/task/fastapi/routing.py", line 165, in run_endpoint_function
    return await run_in_threadpool(dependant.call, **values)
  File "/var/task/starlette/concurrency.py", line 41, in run_in_threadpool
    return await anyio.to_thread.run_sync(func, *args)
  File "/var/task/anyio/to_thread.py", line 33, in run_sync
    return await get_asynclib().run_sync_in_worker_thread(
  File "/var/task/anyio/_backends/_asyncio.py", line 877, in run_sync_in_worker_thread
    return await future
  File "/var/task/anyio/_backends/_asyncio.py", line 807, in run
    result = context.run(func, *args)
  File "/var/task/routes/users.py", line 28, in login
    if user and verify_password(form_data.password, user.password):
  File "/var/task/crud.py", line 15, in verify_password
    return pwd_context.verify(plain_password, hashed_password)
  File "/var/task/passlib/context.py", line 2347, in verify
    return record.verify(secret, hash, **kwds)
  File "/var/task/passlib/utils/handlers.py", line 792, in verify
    return consteq(self._calc_checksum(secret), chk)
  File "/var/task/passlib/handlers/bcrypt.py", line 591, in _calc_checksum
    self._stub_requires_backend()
  File "/var/task/passlib/utils/handlers.py", line 2254, in _stub_requires_backend
    cls.set_backend()
  File "/var/task/passlib/utils/handlers.py", line 2156, in set_backend
    return owner.set_backend(name, dryrun=dryrun)
  File "/var/task/passlib/utils/handlers.py", line 2176, in set_backend
    raise default_error
passlib.exc.MissingBackendError: bcrypt: no backends available -- recommend you install one (e.g. 'pip install bcrypt')
[ERROR] 2023-06-15T11:27:11.426Z bd791912-cd83-4274-b5dd-91c7280b9171 An error occurred running the application. Traceback (most recent call last): File "/var/task/mangum/protocols/http.py", line 58, in run await app(self.scope, self.receive, self.send) File "/var/task/fastapi/applications.py", line 276, in __call__ await super().__call__(scope, receive, send) File "/var/task/starlette/applications.py", line 122, in __call__ await self.middleware_stack(scope, receive, send) File "/var/task/starlette/middleware/errors.py", line 184, in __call__ raise exc File "/var/task/starlette/middleware/errors.py", line 162, in __call__ await self.app(scope, receive, _send) File "/var/task/starlette/middleware/cors.py", line 83, in __call__ await self.app(scope, receive, send) File "/var/task/starlette/middleware/exceptions.py", line 79, in __call__ raise exc File "/var/task/starlette/middleware/exceptions.py", line 68, in __call__ await self.app(scope, receive, sender) File "/var/task/fastapi/middleware/asyncexitstack.py", line 21, in __call__ raise e File "/var/task/fastapi/middleware/asyncexitstack.py", line 18, in __call__ await self.app(scope, receive, send) File "/var/task/starlette/routing.py", line 718, in __call__ await route.handle(scope, receive, send) File "/var/task/starlette/routing.py", line 276, in handle await self.app(scope, receive, send) File "/var/task/starlette/routing.py", line 66, in app response = await func(request) File "/var/task/fastapi/routing.py", line 237, in app raw_response = await run_endpoint_function( File "/var/task/fastapi/routing.py", line 165, in run_endpoint_function return await run_in_threadpool(dependant.call, **values) File "/var/task/starlette/concurrency.py", line 41, in run_in_threadpool return await anyio.to_thread.run_sync(func, *args) File "/var/task/anyio/to_thread.py", line 33, in run_sync return await get_asynclib().run_sync_in_worker_thread( File "/var/task/anyio/_backends/_asyncio.py", line 877, in run_sync_in_worker_thread return await future File "/var/task/anyio/_backends/_asyncio.py", line 807, in run result = context.run(func, *args) File "/var/task/routes/users.py", line 28, in login if user and verify_password(form_data.password, user.password): File "/var/task/crud.py", line 15, in verify_password return pwd_context.verify(plain_password, hashed_password) File "/var/task/passlib/context.py", line 2347, in verify return record.verify(secret, hash, **kwds) File "/var/task/passlib/utils/handlers.py", line 792, in verify return consteq(self._calc_checksum(secret), chk) File "/var/task/passlib/handlers/bcrypt.py", line 591, in _calc_checksum self._stub_requires_backend() File "/var/task/passlib/utils/handlers.py", line 2254, in _stub_requires_backend cls.set_backend() File "/var/task/passlib/utils/handlers.py", line 2156, in set_backend return owner.set_backend(name, dryrun=dryrun) File "/var/task/passlib/utils/handlers.py", line 2176, in set_backend raise default_error passlib.exc.MissingBackendError: bcrypt: no backends available -- recommend you install one (e.g. 'pip install bcrypt')
原因分析
  1. bcrypt是编译型依赖:bcrypt包含C扩展代码,本地安装时会根据你的操作系统(Windows/macOS)编译适配的二进制文件,但AWS Lambda运行在Amazon Linux环境,跨系统编译的二进制文件无法在Lambda中加载,导致依赖失效。
  2. 打包方式不兼容:直接在本地用pip install打包部署包,没有针对Lambda环境处理编译型依赖,导致Lambda无法找到可用的bcrypt后端。
  3. 错误处理掩盖真相:代码中的全局try-except块把MissingBackendError捕获后统一抛出401错误,初期无法发现真实的依赖问题,移除try-except后才触发500错误,通过CloudWatch日志才定位到根源。
修复方案

方法一:用Lambda层安装兼容的bcrypt

  1. 构建适配Lambda环境的bcrypt层:
    • 用Docker启动Amazon Linux 2容器(或直接用EC2实例),模拟Lambda运行环境
    • 创建目录:mkdir -p python/lib/python3.x/site-packages(x替换为你Lambda使用的Python版本,比如3.9)
    • 安装bcrypt:pip install bcrypt -t python/lib/python3.x/site-packages
    • 打包成zip:zip -r bcrypt-layer.zip python/
    • 上传该zip到Lambda层,然后在你的Lambda函数中关联此层。

方法二:用Docker构建Lambda部署包

  1. 编写Dockerfile,基于Lambda官方Python镜像构建:
    FROM public.ecr.aws/lambda/python:3.9
    COPY requirements.txt .
    RUN pip install -r requirements.txt -t "${LAMBDA_TASK_ROOT}"
    COPY app.py ${LAMBDA_TASK_ROOT}
    CMD ["app.handler"]
    
  2. 构建镜像并推送到ECR,再从ECR部署到Lambda,这样依赖会在Lambda兼容的环境中编译安装。

方法三:替换为纯Python哈希库

如果不想处理编译型依赖的兼容性问题,可以改用纯Python实现的哈希算法,比如passlib的pbkdf2_sha256:

  1. 修改密码哈希上下文配置:
    from passlib.context import CryptContext
    
    pwd_context = CryptContext(schemes=["pbkdf2_sha256"], deprecated="auto")
    
  2. 重新生成现有用户的密码哈希(如果有存量数据),再重新部署代码。

额外优化:改进错误处理

去掉全局try-except,改为捕获特定异常,避免掩盖真实错误:

@route.post("/login", tags=['Account'])
def login(form_data: OAuth2PasswordRequestForm = Depends()):
    try:
        user = db.query(UserModel).filter_by(email=form_data.username).first()
        if not user:
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password")
        if not verify_password(form_data.password, user.password):
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password")
        return {"access_token": signJWT(user), "token_type": "Bearer"}
    except SQLAlchemyError:
        db.rollback()
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Database error")

内容的提问来源于stack exchange,提问作者Sardar Jagpreet Singh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 23:54:55