사용자 지정 미들웨어 때문에 Fast API Swager가 렌더링되지 않습니까?
다음과 같은 맞춤형 미들웨어가 있습니다:
그것의 목적은 myFast의 모든 엔드포인트의 모든 응답에 meta_data 필드를 추가하는 것이다API 앱.
@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을 사용하여 앱을 서비스하고 swager 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")
내가 이 대사를 간단히 언급한다면, 스웨거는 나에게 딱 좋다. 하지만, 저는 미들웨어로부터의 응답에서 내용 인수를 전달하기 위해 이 줄이 필요합니다. 어떻게 해결해야 할까요?
업데이트:
나는 다음을 시도했다: 기본 arg를 제거함으로써 swager가 성공적으로 로드되었다. 그러나 이제 내가 API 중 하나를 클릭하면, 화면의 응답 페이로드와 함께 swager가 나에게 알려주는 것은 다음과 같다:
추가 업데이트(2022년 4월 6일):
크리스가 문제의 한 부분을 해결할 해결책을 얻었지만, 스웨거는 여전히 로딩되지 않았습니다. 코드가 미들웨어 수준에서 무기한 중단되었으며 페이지가 여전히 로드되지 않았습니다.
그래서, 나는 이 모든 곳에서 발견했다:
- https://github.com/encode/starlette/issues/919
- 미들웨어 및 종속성 주입을 사용하여 FastAPI(Python)에 요청을 기록하는 동안 차단된 코드
- https://github.com/tiangolo/fastapi/issues/394
사용자 지정 미들웨어를 추가하는 이 방법은 Base에서 상속함으로써 작동합니다Stallette의 HTTP 미들웨어는 자체적인 문제(내부 미들웨어 대기, 스트리밍 응답 및 정상 응답, 호출 방식과 관련이 있음)를 가지고 있다. 아직 이해가 안 돼요.
swagger html의 본문을 미들웨어와 응답에서 가져온 json 데이터(이 경우 html 응답)로 대체하고 있습니다.
당신은 결국 다음과 같은 일을 겪게 될 것이다
{
"data": "<html>....</html>",
"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"
}
}
물론 이것은 효과가 없을 것이다.
가능한 해결책
미들웨어에서 응답 내용 유형을 확인합니다. 응답이 있는 경우 응답을 확장하고, 그렇지 않은 경우 응답을 그대로 둡니다.
참고: 이 작업은 모든 응답에 가 추가되어야 하지만 내용 유형에는 추가되지 않는다고 가정할 수 있는 경우에만 수행할 수 있습니다. (필요에 따라 수표를 변경할 수 있습니다.)
다른 가능한 해결책
다음 문제가 현재 구현에 병합되고 이 버전의 사용을 시작할 때까지 기다립니다.
https://github.com/tiangolo/fastapi/issues/1174 https://github.com/encode/starlette/pull/1286
여기에 영감을 받아 그렇게 할 수 있는 방법이 있습니다. 유형인 경우에만 를 추가하여 수정할 수 있도록 아래와 같이 응답의 를 확인하십시오.
OpenAPI(Swagger UI)가 렌더링(및 둘 다)하려면 응답에 키가 없는지 확인하여 해당 경우에만 응답 수정을 진행할 수 있습니다. 응답 데이터에 이러한 이름을 가진 키가 있으면 OpenAPI에 대한 응답에 있는 추가 키를 사용하여 추가로 확인할 수 있습니다(예: , , ). 필요한 경우 해당 값과 비교하여 확인할 수도 있습니다.
from fastapi import FastAPI, Request, Response
import json
app = FastAPI()
@app.middleware("http")
async def add_metadata_to_response_payload(request: Request, call_next):
response = await call_next(request)
content_type = response.headers.get('Content-Type')
if content_type == "application/json":
response_body = [section async for section in response.body_iterator]
resp_str = response_body[0].decode() # converts "response_body" bytes into string
resp_dict = json.loads(resp_str) # converts resp_str into dict
#print(resp_dict)
if "openapi" not in resp_dict:
data = {}
data["data"] = resp_dict # adds the "resp_dict" to the "data" dictionary
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"}
resp_str = json.dumps(data, indent=2) # converts dict into JSON string
return Response(content=resp_str, status_code=response.status_code, media_type=response.media_type)
return response
@app.get("/")
def foo(request: Request):
return {"hello": "world!"}
업데이트 1
또는 미들웨어 기능을 시작할 때 요청의 URL 경로를 확인하고(응답에 메타데이터를 추가하려는 미리 정의된 경로/경로 목록과 비교하여) 그에 따라 진행하는 것이 더 좋습니다. 다음은 예를 제시하겠습니다.
from fastapi import FastAPI, Request, Response, Query
from pydantic import constr
from fastapi.responses import JSONResponse
import re
import uvicorn
import json
app = FastAPI()
routes_with_middleware = ["/"]
rx = re.compile(r'^(/items/\d+|/courses/[a-zA-Z0-9]+)$') # support routes with path parameters
my_constr = constr(regex="^[a-zA-Z0-9]+$")
@app.middleware("http")
async def add_metadata_to_response_payload(request: Request, call_next):
response = await call_next(request)
if request.url.path not in routes_with_middleware and not rx.match(request.url.path):
return response
else:
content_type = response.headers.get('Content-Type')
if content_type == "application/json":
response_body = [section async for section in response.body_iterator]
resp_str = response_body[0].decode() # converts "response_body" bytes into string
resp_dict = json.loads(resp_str) # converts resp_str into dict
data = {}
data["data"] = resp_dict # adds "resp_dict" to the "data" dictionary
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"}
resp_str = json.dumps(data, indent=2) # converts dict into JSON string
return Response(content=resp_str, status_code=response.status_code, media_type="application/json")
return response
@app.get("/")
def root():
return {"hello": "world!"}
@app.get("/items/{id}")
def get_item(id: int):
return {"Item": id}
@app.get("/courses/{code}")
def get_course(code: my_constr):
return {"course_code": code, "course_title": "Deep Learning"}
업데이트 2
또 다른 해결책은 설명된 대로 및 를 사용하여 본문의 변경 사항을 사용자가 지정한 경로에 적용할 수 있으며, 이를 통해 Swager UI의 문제를 더 쉽게 해결할 수 있습니다.
원하는 경우에도 미들웨어 옵션을 사용할 수 있지만 메인에 미들웨어를 추가하는 대신 본문에 일부 데이터를 추가하기 위해 에서 수정해야 하는 경로를 다시 포함하는 에 미들웨어 옵션을 추가할 수 있습니다( 참조).
'개발하자' 카테고리의 다른 글
MongoDB와 관련된 FastAPI 문제 - TypeError: 'ObjectId' 개체를 인식할 수 없습니다. (0) | 2023.01.15 |
---|---|
Fast API - 필드 필수, 누락된 값 (0) | 2023.01.14 |
대화형으로 IPython/Jupyter 노트북 실행 (0) | 2023.01.13 |
FastAPI가 정적 파일을 로드하지 않음 (0) | 2023.01.12 |
kubernetes에 배포된 mongodb의 암호 변경 (1) | 2023.01.12 |