пользовательский класс ответа fastapi как класс ответа по умолчанию

#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