|
5 | 5 | import inspect
|
6 | 6 | from typing import Any, Callable, Dict, List, Optional, Type, TypedDict, Union
|
7 | 7 |
|
8 |
| -from fastapi import Depends, params |
9 |
| -from fastapi.dependencies.utils import get_parameterless_sub_dependant |
| 8 | +from fastapi import Depends, FastAPI, params |
| 9 | +from fastapi.datastructures import DefaultPlaceholder |
| 10 | +from fastapi.dependencies.utils import get_dependant, get_parameterless_sub_dependant |
| 11 | +from fastapi.routing import APIRoute |
10 | 12 | from pydantic import BaseModel
|
11 | 13 | from starlette.concurrency import run_in_threadpool
|
12 | 14 | from starlette.requests import Request
|
13 | 15 | from starlette.responses import Response
|
14 |
| -from starlette.routing import BaseRoute, Match |
| 16 | +from starlette.routing import BaseRoute, Match, request_response |
15 | 17 | from starlette.status import HTTP_204_NO_CONTENT
|
16 | 18 |
|
17 | 19 | from stac_fastapi.api.models import APIRequest
|
@@ -131,3 +133,33 @@ def add_route_dependencies(
|
131 | 133 | # https://github.com/tiangolo/fastapi/blob/58ab733f19846b4875c5b79bfb1f4d1cb7f4823f/fastapi/applications.py#L337-L360
|
132 | 134 | # https://github.com/tiangolo/fastapi/blob/58ab733f19846b4875c5b79bfb1f4d1cb7f4823f/fastapi/routing.py#L677-L678
|
133 | 135 | route.dependencies.extend(dependencies)
|
| 136 | + |
| 137 | + |
| 138 | +def add_direct_response(app: FastAPI) -> None: |
| 139 | + """ |
| 140 | + Setup FastAPI application's endpoints to return Response Object directly, avoiding |
| 141 | + Pydantic validation and FastAPI (slow) serialization. |
| 142 | +
|
| 143 | + ref: https://gist.github.com/Zaczero/00f3a2679ebc0a25eb938ed82bc63553 |
| 144 | + """ |
| 145 | + |
| 146 | + def wrap_endpoint(endpoint: Callable, cls: Type[Response]): |
| 147 | + @functools.wraps(endpoint) |
| 148 | + async def wrapper(*args, **kwargs): |
| 149 | + content = await endpoint(*args, **kwargs) |
| 150 | + return content if isinstance(content, Response) else cls(content) |
| 151 | + |
| 152 | + return wrapper |
| 153 | + |
| 154 | + for route in app.routes: |
| 155 | + if not isinstance(route, APIRoute): |
| 156 | + continue |
| 157 | + |
| 158 | + response_class = route.response_class |
| 159 | + if isinstance(response_class, DefaultPlaceholder): |
| 160 | + response_class = response_class.value |
| 161 | + |
| 162 | + if issubclass(response_class, Response): |
| 163 | + route.endpoint = wrap_endpoint(route.endpoint, response_class) |
| 164 | + route.dependant = get_dependant(path=route.path_format, call=route.endpoint) |
| 165 | + route.app = request_response(route.get_route_handler()) |
0 commit comments