FastAPI 中的不同 OpenAPI 模式取决于环境

Different OpenAPI schema in FastAPI depending on environment

我们有一个托管在反向代理后面的 FastApi 应用程序。

代理使用 Kerberos 对用户进行身份验证,并向请求添加 X-Remote-User HTTP header。

FastApi 应用程序需要此 header。这是一个示例路线:

@app.get("/user/me")
async def get_user_me(x_remote_user: str = Header(...)):
    return {"User": x_remote_user}

请求需要 X-Remote-User header,这是预期的行为。

当我们现在打开 Swagger Ui 时,记录了 header,当单击“试用”时,我们可以提供 header 值。 这种行为非常适合开发,但在所有其他情况下都是不希望的,因为 header 是由反向代理提供的。例如,我们使用 OpenAPI Generator 生成客户端,然后客户端都需要在其请求中使用 X-Remote-User 参数。

因此,拥有区分环境的配置会很有用。如果我们在反向代理后面,那么 FastApi 生成的 OpenAPI Schema 不应该包含 X-Remote-Header,否则如果我们在开发中,它应该包含。

到目前为止我做了什么:

有没有人有解决这个问题的好方法?

一种方法是生成 OpenApi 架构,如文档 Extending OpenAPI 中所述。生成后,从架构中删除 X-Remote-User。在配置中可以是一个标志,表明应用程序在反向代理后面有条件地执行代码:

from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi
from MyConfig import Config


app = FastAPI()


@app.get("/items/")
async def read_items():
    return [{"name": "Foo"}]

if Config.reverse_proxy:
    def custom_openapi():
        if app.openapi_schema:
            return app.openapi_schema
        openapi_schema = get_openapi(
            title="Custom title",
            version="2.5.0",
            description="This is a very custom OpenAPI schema",
            routes=app.routes,
        )
        // remove X-Remote-User here
        app.openapi_schema = openapi_schema
        return app.openapi_schema


    app.openapi = custom_openapi

然而,这不是一个非常优雅的解决方案,因为我们需要解析 Json 字符串并删除 X-Remote-User header 的不同 deeply-nested 处。这很容易出现导致无效模式的错误。此外,如果添加新的 Rest 端点,它可能会中断。

我们可以使用 APIKeyHeader 从 API 签名中删除 X-Remote-User header,但仍然强制存在 header。

from fastapi.security import APIKeyHeader

apiKey = APIKeyHeader(name="X-Remote-User")

@app.get("/user/me")
async def get_user_me(x_remote_user: str = Depends(apiKey)):
    return {"User": x_remote_user}

当 header 不存在时,我们会收到“403 Forbidden”。如果存在,我们检索 header 值。

Swagger UI 现在有一个“授权”按钮,我们可以在其中 fill-in X-Remote-User 的值以进行测试。

一个新参数将很快可用于 HeaderQuery 和其他以从 openAPI 输出中排除元素:include_in_schema=False

示例:

def test(x_forwarded_for: str = Header(None, include_in_schema=False)):
    ...

这里是补丁状态:https://github.com/tiangolo/fastapi/pull/3144