【问题标题】:FastAPI RedirectResponse gets {"message": "Forbidden"} when redirecting to a different routeFastAPI RedirectResponse 在重定向到不同的路由时获取 {\"message\": \"Forbidden\"}
【发布时间】:2022-07-28 23:56:50
【问题描述】:

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

我有一个使用 Docker、无服务器并部署在 AWS API Gateway 上的 FastAPI 的 API 设置。讨论的所有路由都使用传递到标头 (x-api-key) 的 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)

因此,我们检查某个值是否为真/假,如果为假,我们使用RedirectResponse()abc/item 重定向到xyz/item。我们传递了redirect_url,它只是包含查询参数的xyz/item 路由,我们传递了request.headers(如建议的herehere),因为我认为我们需要将x-api-key 传递到新路由.在第二条路线中,我们再次尝试在不同的表 (other_items) 中进行查询并返回一些值。

我还尝试将status_code=status.HTTP_303_SEE_OTHERstatus_code=status.HTTP_307_TEMPORARY_REDIRECT 传递给RedirectResponse(),正如我在StackOverflow FastAPI discussions 上发现的一些相关问题所建议的那样,但这也无济于事。

@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/itemFalse(因此它应该重定向)我得到{\"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 错误。可惜我没看到任何事物当我在 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_itemsitems 表所需的两个不同的数据库连接,尽管我不确定这有多相关,考虑到在本地重定向时这个功能很好。为此,我实施了找到here 的解决方案。这个自定义中间件首先是重定向的原因(我们使用该中间件根据路由更改连接 URI),所以我认为分享这些信息也很好。

谢谢!

    标签: docker aws-api-gateway amazon-cloudfront fastapi serverless


    【解决方案1】:

    herehere 所述,重定向到设置了自定义标题的页面是不可能的。 HTTP 协议中的重定向不支持将任何标头添加到目标位置。它本身基本上只是一个标头,只允许一个 URL。当您将authorization 标头添加到RedirectResponse 时,您只是将该标头发送回客户端。

    建议使用here,您可以使用set-cookie HTTP 响应标头:

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

    在 FastAPI 中 - 可以找到 herehere 的文档 - 这可以按如下方式完成:

    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 - 您可以在 request.cookies 下找到它,它应该返回,例如,您可以使用 request.cookies.get('fakesession') 检索的 {'fakesession': 'fake-cookie-session-value-MANUAL'} - 以进行身份​​验证用户。

    另一方面,request.url_for() 函数只接受path 参数,而不接受query 参数(例如/abc/item/xyz/item 端点中的item_id)。因此,您可以按照您已经做的方式创建 URL,或者使用CustomURLProcessor 建议的hereherehere,它允许您同时传递pathquery 参数。

    【讨论】:

    • 非常感谢!我有两个问题。 1]我应该有什么特别的东西作为我的cookie“键”和“值”吗? 2] 在我的xyz/item 路由中执行request.headers['cookie'] 时,我该如何处理这些cookie,您提到要对用户进行身份验证,但我不确定如何在应用程序本身内执行此操作(由无服务器处理马上)
    • 我仍然不太清楚我应该如何根据您的建议完成我的任务。我试过:response.set_cookie(key="x-api-key", value="XXX"),我已经循环了所有的requests.headers.items(),并为每个键值对设置了response.set_cookies()。而且,我尝试将path=redirct_url 添加到set_cookie() 旁边。我仍然遇到同样的错误,我不确定这是因为我设置了错误的 cookie,还是因为我在 xyz/item 中没有正确验证(我没有在那里更改任何内容,因为我没有'不知道我应该改变什么......)。
    猜你喜欢
    • 2020-11-02
    • 1970-01-01
    • 2023-01-22
    • 2020-09-28
    • 2021-08-07
    • 1970-01-01
    • 2015-11-09
    • 2021-09-09
    • 1970-01-01
    相关资源
    最近更新 更多