我有一个定制的中间件,如下所示:
它的目标是向来自FastAPI应用程序所有端点的每个响应添加一些 meta_data字段。
@app.middelware("http")
async def add_metadata_to_response_payload(request: Request, call_next):
response = await call_next(request)
body = b""
async for chunk in response.body_iterator:
body+=chunk
data = {}
data["data"] = json.loads(body.decode())
data["metadata"] = {
"some_data_key_1": "some_data_value_1",
"some_data_key_2": "some_data_value_2",
"some_data_key_3": "some_data_value_3"
}
body = json.dumps(data, indent=2, default=str).encode("utf-8")
return Response(
content=body,
status_code=response.status_code,
media_type=response.media_type
)
然而,当我使用uvicorn服务我的应用程序,并启动swagger URL时,我看到了以下内容:
Unable to render this definition
The provided definition does not specify a valid version field.
Please indicate a valid Swagger or OpenAPI version field. Supported version fields are
Swagger: "2.0" and those that match openapi: 3.0.n (for example, openapi: 3.0.0)
经过大量的调试,我发现这个错误是由于自定义中间件,特别是这一行:
body = json.dumps(data, indent=2, default=str).encode("utf-8")
如果我简单地注解掉这一行,swagger就能很好地呈现。但是,我需要这一行来传递来自中间件的响应中的内容参数。如何解决这个问题?
更新:
我尝试了以下方法:body = json.dumps(data, indent=2).encode("utf-8")
通过删除默认的arg,swagger确实成功加载了。但是现在当我点击任何一个API时,swagger会告诉我以下内容沿着屏幕上的响应负载:Unrecognised response type; displaying content as text
更多更新(2022年4月6日):
Chris提供了一个解决方案来修复问题的一部分,但是swagger仍然无法加载,代码被无限期地挂在中间件层,页面也无法加载。
所以,我在所有这些地方发现:
- https://github.com/encode/starlette/issues/919
- Blocked code while using middleware and dependency injections to log requests in FastAPI(Python)
- https://github.com/tiangolo/fastapi/issues/394
这种添加自定义中间件的方式是继承Starlette的BaseHTTPMiddleware,有自己的问题(中间件内部等待、streamingresponse和normal response、调用方式等问题),我还不明白。
2条答案
按热度按时间4dbbbstv1#
下面是您如何做到这一点(受此启发):确保检查响应的
Content-Type
(如下所示),以便您可以通过添加metadata
来修改它,只有当它是application/json
类型时。对于OpenAPI( Swagger UI)渲染(
/docs
和/redoc
),请确保检查openapi
密钥是否不存在于响应中,以便只有在这种情况下才能继续修改响应。如果您的响应数据中碰巧有这样一个名称的密钥,则可以使用OpenAPI响应中存在的其他密钥进行额外检查,例如info
、version
、paths
,如果需要,您也可以检查它们的值。更新1
或者,一个可能更好的方法是在中间件函数开始时检查请求的url路径(对照一个预定义的路径/路由列表,您希望将元数据添加到它们的响应中),并相应地进行操作。
更新2
另一个解决方案是使用定制的
APIRoute
类,如here和here所示,这将允许您将response
主体上的更改仅应用于您指定的路由-这将以更简单的方式解决Swaager UI的问题。如果愿意,您仍然可以使用中间件选项,但不是将中间件添加到主
app
中,而是将其添加到子应用程序中(如this answer和this answer所示),该子应用程序再次仅包含您需要修改response
以便在主体中添加一些额外数据的路由。pkbketx92#
您将用取自中间件和响应(本例中为HTML响应)的JSON数据替换swagger HTML的主体。
你最终会得到这样的结果
这当然行不通。
可能的解决方案
检查中间件中响应的内容类型,如果是
json
,则扩展响应,否则保持不变。注意:只有当可以安全地假设每个
json
响应都需要添加metadata
,而html
内容类型不需要时,才可以这样做。(您可以根据需要更改检查)另一个可能的解决方案
等待以下问题合并到当前
starlette
的实现中,然后fastapi
开始使用此版本。https://github.com/tiangolo/fastapi/issues/1174https://github.com/encode/starlette/pull/1286