#python #python-3.x #fastapi
#python #python-3.x #fastapi
Вопрос:
Я пытаюсь использовать пользовательский класс ответа в качестве ответа по умолчанию.
from fastapi.responses import Response
from bson.json_util import dumps
class MongoResponse(Response):
def __init__(self, content, *args, **kwargs):
super().__init__(
content=dumps(content),
media_type="application/json",
*args,
**kwargs,
)
Это отлично работает, когда я явно использую класс ответа.
@app.get("/")
async def getDoc():
foo = client.get_database('foo')
result = await foo.bar.find_one({'author': 'fool'})
return MongoResponse(result)
Однако, когда я пытаюсь передать это в качестве аргумента конструктору FastAPI, похоже, что он не используется только при возврате данных из обработчика запроса.
app = FastAPI(default_response_class=MongoResponse)
@app.get("/")
async def getDoc():
foo = client.get_database('foo')
result = await foo.bar.find_one({'author': 'fool'})
return result
Когда я смотрю на трассировку стека ниже, кажется, что он все еще использует обычный ответ по умолчанию, который является ответом json.
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "/home/blue/podman/test/.venv/lib/python3.6/site-packages/uvicorn/protocols/http/httptools_impl.py", line 390, in run_asgi
result = await app(self.scope, self.receive, self.send)
File "/home/blue/podman/test/.venv/lib/python3.6/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
return await self.app(scope, receive, send)
File "/home/blue/podman/test/.venv/lib/python3.6/site-packages/fastapi/applications.py", line 181, in __call__
await super().__call__(scope, receive, send) # pragma: no cover
File "/home/blue/podman/test/.venv/lib/python3.6/site-packages/starlette/applications.py", line 111, in __call__
await self.middleware_stack(scope, receive, send)
File "/home/blue/podman/test/.venv/lib/python3.6/site-packages/starlette/middleware/errors.py", line 181, in __call__
raise exc from None
File "/home/blue/podman/test/.venv/lib/python3.6/site-packages/starlette/middleware/errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "/home/blue/podman/test/.venv/lib/python3.6/site-packages/starlette/exceptions.py", line 82, in __call__
raise exc from None
File "/home/blue/podman/test/.venv/lib/python3.6/site-packages/starlette/exceptions.py", line 71, in __call__
await self.app(scope, receive, sender)
File "/home/blue/podman/test/.venv/lib/python3.6/site-packages/starlette/routing.py", line 566, in __call__
await route.handle(scope, receive, send)
File "/home/blue/podman/test/.venv/lib/python3.6/site-packages/starlette/routing.py", line 227, in handle
await self.app(scope, receive, send)
File "/home/blue/podman/test/.venv/lib/python3.6/site-packages/starlette/routing.py", line 41, in app
response = await func(request)
File "/home/blue/podman/test/.venv/lib/python3.6/site-packages/fastapi/routing.py", line 199, in app
is_coroutine=is_coroutine,
File "/home/blue/podman/test/.venv/lib/python3.6/site-packages/fastapi/routing.py", line 122, in serialize_response
return jsonable_encoder(response_content)
File "/home/blue/podman/test/.venv/lib/python3.6/site-packages/fastapi/encoders.py", line 94, in jsonable_encoder
sqlalchemy_safe=sqlalchemy_safe,
File "/home/blue/podman/test/.venv/lib/python3.6/site-packages/fastapi/encoders.py", line 139, in jsonable_encoder
raise ValueError(errors)
ValueError: [TypeError("'ObjectId' object is not iterable",), TypeError('vars() argument must have __dict__ attribute',)]
Ответ №1:
Таким образом, оказывается, что класс ответа по умолчанию, а также класс ответа на маршруте существуют только для документации open API. По умолчанию документация будет документировать каждую конечную точку, как если бы они возвращали json.
Таким образом, в приведенном ниже примере кода каждый ответ будет помечен как тип содержимого text / html. Во втором маршруте это перезаписывается с помощью application / json
app = FastAPI(default_response_class=HTMLResponse)
@app.get("/")
async def getDoc():
foo = client.get_database('foo')
result = await foo.bar.find_one({'author': 'Mike'})
return MongoResponse(result)
@app.get("/other", response_class=JSONResponse)
async def json():
return {"json": "true"}
В этом смысле я, вероятно, должен явно использовать свой класс и оставить класс ответов по умолчанию как JSON, чтобы они документировались как ответы JSON.
Ответ №2:
Я прибегнул к исправлению ошибок
from fastapi import routing as fastapi_routing
from fastapi.responses import ORJSONResponse
def api_route(self, path, **kwargs):
def decorator(func):
if type(kwargs["response_class"]) == DefaultPlaceholder:
kwargs["response_class"] = Default(ORJSONResponse)
self.add_api_route(
path,
func,
**kwargs,
)
return func
return decorator
fastapi_routing.APIRouter.api_route = api_route