重定向到不同路由时,FastAPI RedirectResponse 获取 {"message": "Forbidden"}

2024-03-21

请向我提出一个几乎不可能创建可重现示例的问题。

我使用 Docker、无服务器和部署在 AWS API Gateway 上的 FastAPI 设置了 API。讨论的所有路由都受到传递到标头中的 api-key 的保护(x-api-key).

我正在尝试使用以下命令完成从一条路线到另一条路线的简单重定向fastapi.responses.RedirectResponse。重定向在本地工作得很好(不过,这是没有 api-key 的),并且当部署在 AWS 上并直接连接时,两条路由都工作得很好,但有些东西阻止了路由一的重定向(abc/item) 路由两条 (xyz/item)当我部署到 AWS 时。我不确定可能是什么问题,因为 CloudWatch 中的日志没有给我太多可处理的信息。

为了说明我的问题,假设我们有路线abc/item看起来像这样:

@router.get("/abc/item")
async def get_item(item_id: int, request: Request, db: Session = Depends(get_db)):

    if False:
        redirect_url = f"/xyz/item?item_id={item_id}"
        logging.info(f"Redirecting to {redirect_url}")
        return RedirectResponse(redirect_url, headers=request.headers)
    else:
        execution = db.execute(text(items_query))
        return convert_to_json(execution)

因此,我们检查某个值是否为 True/False,如果为 False,我们会从abc/item to xyz/item using RedirectResponse()。我们传递redirect_url,这只是xyz/item路线包括查询参数,我们通过request.headers(按照建议here https://stackoverflow.com/questions/62282100/fastapi-redirectresponse-custom-headers and here https://stackoverflow.com/questions/68231936/python-fastapi-how-can-i-get-headers-or-a-specific-header-from-my-backend-api),因为我认为我们需要传递x-api-key到新路线。在第二条路线中,我们再次尝试在不同的表中进行查询(other_items)并返回一些值。

我也尝试过通过status_code=status.HTTP_303_SEE_OTHER and status_code=status.HTTP_307_TEMPORARY_REDIRECT to RedirectResponse()正如我发现的一些与切线相关的问题所建议的堆栈溢出 https://stackoverflow.com/questions/66849929/fastapi-redirect-gives-method-not-allowed-errorFastAPI 讨论 https://github.com/tiangolo/fastapi/issues/1498,但这也没有帮助。

@router.get("/xyz/item")
async def get_item(item_id: int, db: Session = Depends(get_db)):

    execution = db.execute(text(other_items_query))
    return convert_to_json(execution)

就像我说的,部署后我可以成功地直接连接到两者abc/item并得到一个返回值,如果True我还可以连接到xyz/item直接并从中获取正确的值,但是当我将值传递给abc/item那是False(因此它应该重定向)我明白{"message": "Forbidden"}.

如果它有任何帮助,我尝试使用“curl”工具对此进行调试,并且返回的标头提供以下信息:

Content-Type: application/json
Content-Length: 23
Connection: keep-alive
Date: Wed, 27 Jul 2022 08:43:06 GMT
x-amzn-RequestId: XXXXXXXXXXXXXXXXXXXX
x-amzn-ErrorType: ForbiddenException
x-amz-apigw-id: XXXXXXXXXXXXXXXX
X-Cache: Error from cloudfront
Via: 1.1 XXXXXXXXXXXXXXXXXXXXXXXXX.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: XXXXX
X-Amz-Cf-Id: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

因此,这暗示存在 CloudFront 错误。不幸的是我没有看到anything当我查看 AWS 上的 CloudFront 仪表板时,稍微暗示了这个 API,那里实际上什么也没有(不过我确实有权查看内容......)

CloudWatch 中的 API 日志如下所示:

2022-07-27T03:43:06.495-05:00   Redirecting to /xyz/item?item_id=1234...
2022-07-27T03:43:06.495-05:00   [INFO] 2022-07-27T08:43:06.495Z Redirecting to /xyz/item?item_id=1234...
2022-07-27T03:43:06.496-05:00   2022-07-27 08:43:06,496 INFO sqlalchemy.engine.Engine ROLLBACK
2022-07-27T03:43:06.496-05:00   [INFO] 2022-07-27T08:43:06.496Z ROLLBACK
2022-07-27T03:43:06.499-05:00   END RequestId: 6f449762-6a60189e4314
2022-07-27T03:43:06.499-05:00   REPORT RequestId: 6f449762-6a60189e4314 Duration: 85.62 ms Billed Duration: 86 ms Memory Size: 256 MB Max Memory Used: 204 MB

我一直想知道我的问题是否与我需要添加到我的某个地方的东西有关serverless.yml,也许在functions:部分。目前这两条路线看起来像这样:

    events:
     - http:
          path: abc/item
          method: get
          cors: true
          private: true
          request:
            parameters:
              querystrings:
                item_id: true
      - http:
          path: xyz/item
          method: get
          cors: true
          private: true
          request:
            parameters:
              querystrings:
                item_id: true

最后,值得注意的是,我已将自定义中间件添加到 FastAPI 来处理连接到的两个不同的数据库连接other_items and items表,尽管我不确定这有多相关,但考虑到在本地重定向时这个功能很好。为此我实施了找到的解决方案here https://stackoverflow.com/questions/70081977/multiple-database-connections-using-fastapi。这个自定义中间件首先是重定向的原因(我们根据该中间件的路由更改连接 URI),所以我认为共享这些信息也很好。

Thanks!


如前所述here https://github.com/expressjs/express/issues/3551#issuecomment-362605750 and here https://stackoverflow.com/a/41218304/17865804,不可能重定向到设置了自定义标头的页面。重定向在HTTP协议不支持向目标位置添加任何标头。它本身基本上只是一个标头,并且只允许 URL(重定向响应,但如果需要,也可以包含正文内容 — 请参阅这个答案 https://stackoverflow.com/a/74292752/17865804)。当您添加authorization标头到RedirectResponse,您只需将该标头发送回客户端。

一个建议here https://github.com/expressjs/express/issues/3551#issuecomment-623744065,你可以使用set-cookie https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-CookieHTTP 响应头:

The Set-CookieHTTP 响应标头用于从 服务器发送给用户代理(客户端),以便用户代理可以将其发送回 稍后服务器。

在 FastAPI 中可以找到文档here https://fastapi.tiangolo.com/advanced/response-cookies/ and here https://www.starlette.io/responses/#set-cookie—这可以按如下方式完成:

from fastapi import Request
from fastapi.responses import RedirectResponse

@app.get("/abc/item")
def get_item(request: Request):
    redirect_url = request.url_for('your_endpoints_function_name') #e.g., 'get_item'
    response = RedirectResponse(redirect_url)
    response.set_cookie(key="fakesession", value="fake-cookie-session-value", httponly=True)
    return response

在将用户重定向到的另一个端点内,您可以提取该端点cookie对用户进行身份验证。该 cookie 可以在以下位置找到:request.cookies- 例如,应该返回,{'fakesession': 'fake-cookie-session-value-MANUAL'}-然后你使用它来检索它request.cookies.get('fakesession').

另一方面,request.url_for()函数只接受path参数,不query参数(如item_id在你的/abc/item and /xyz/item端点)。因此,您可以按照已有的方式创建 URL,或者使用CustomURLProcessor建议here https://stackoverflow.com/a/70633588/17865804, here https://stackoverflow.com/a/71068465/17865804 and here https://stackoverflow.com/a/73088816/17865804,这使您可以通过path and query参数。

如果重定向发生从一个域到另一个域(例如,从abc.com to xyz.com),请看一下这个答案 https://stackoverflow.com/a/73599289/17865804.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

重定向到不同路由时,FastAPI RedirectResponse 获取 {"message": "Forbidden"} 的相关文章

随机推荐