|
|
|
@ -329,6 +329,7 @@ def get_request_handler( |
|
|
|
response_model_exclude_none: bool = False, |
|
|
|
dependency_overrides_provider: Any | None = None, |
|
|
|
embed_body_fields: bool = False, |
|
|
|
strict_content_type: bool = True, |
|
|
|
) -> Callable[[Request], Coroutine[Any, Any, Response]]: |
|
|
|
assert dependant.call is not None, "dependant.call must be a function" |
|
|
|
is_coroutine = dependant.is_coroutine_callable |
|
|
|
@ -370,7 +371,8 @@ def get_request_handler( |
|
|
|
json_body: Any = Undefined |
|
|
|
content_type_value = request.headers.get("content-type") |
|
|
|
if not content_type_value: |
|
|
|
json_body = await request.json() |
|
|
|
if not strict_content_type: |
|
|
|
json_body = await request.json() |
|
|
|
else: |
|
|
|
message = email.message.Message() |
|
|
|
message["content-type"] = content_type_value |
|
|
|
@ -599,6 +601,7 @@ class APIRoute(routing.Route): |
|
|
|
openapi_extra: dict[str, Any] | None = None, |
|
|
|
generate_unique_id_function: Callable[["APIRoute"], str] |
|
|
|
| DefaultPlaceholder = Default(generate_unique_id), |
|
|
|
strict_content_type: bool = True, |
|
|
|
) -> None: |
|
|
|
self.path = path |
|
|
|
self.endpoint = endpoint |
|
|
|
@ -625,6 +628,7 @@ class APIRoute(routing.Route): |
|
|
|
self.callbacks = callbacks |
|
|
|
self.openapi_extra = openapi_extra |
|
|
|
self.generate_unique_id_function = generate_unique_id_function |
|
|
|
self.strict_content_type = strict_content_type |
|
|
|
self.tags = tags or [] |
|
|
|
self.responses = responses or {} |
|
|
|
self.name = get_name(endpoint) if name is None else name |
|
|
|
@ -713,6 +717,7 @@ class APIRoute(routing.Route): |
|
|
|
response_model_exclude_none=self.response_model_exclude_none, |
|
|
|
dependency_overrides_provider=self.dependency_overrides_provider, |
|
|
|
embed_body_fields=self._embed_body_fields, |
|
|
|
strict_content_type=self.strict_content_type, |
|
|
|
) |
|
|
|
|
|
|
|
def matches(self, scope: Scope) -> tuple[Match, Scope]: |
|
|
|
@ -963,6 +968,29 @@ class APIRouter(routing.Router): |
|
|
|
""" |
|
|
|
), |
|
|
|
] = Default(generate_unique_id), |
|
|
|
strict_content_type: Annotated[ |
|
|
|
bool, |
|
|
|
Doc( |
|
|
|
""" |
|
|
|
Enable strict checking for request Content-Type headers. |
|
|
|
|
|
|
|
When `True` (the default), requests with a body that do not include |
|
|
|
a `Content-Type` header will **not** be parsed as JSON. |
|
|
|
|
|
|
|
This prevents potential cross-site request forgery (CSRF) attacks |
|
|
|
that exploit the browser's ability to send requests without a |
|
|
|
Content-Type header, bypassing CORS preflight checks. In particular |
|
|
|
applicable for apps that need to be run locally (in localhost). |
|
|
|
|
|
|
|
When `False`, requests without a `Content-Type` header will have |
|
|
|
their body parsed as JSON, which maintains compatibility with |
|
|
|
certain clients that don't send `Content-Type` headers. |
|
|
|
|
|
|
|
Read more about it in the |
|
|
|
[FastAPI docs for Strict Content-Type](https://fastapi.tiangolo.com/advanced/strict-content-type/). |
|
|
|
""" |
|
|
|
), |
|
|
|
] = True, |
|
|
|
) -> None: |
|
|
|
# Determine the lifespan context to use |
|
|
|
if lifespan is None: |
|
|
|
@ -1009,6 +1037,7 @@ class APIRouter(routing.Router): |
|
|
|
self.route_class = route_class |
|
|
|
self.default_response_class = default_response_class |
|
|
|
self.generate_unique_id_function = generate_unique_id_function |
|
|
|
self.strict_content_type = strict_content_type |
|
|
|
|
|
|
|
def route( |
|
|
|
self, |
|
|
|
@ -1059,6 +1088,7 @@ class APIRouter(routing.Router): |
|
|
|
openapi_extra: dict[str, Any] | None = None, |
|
|
|
generate_unique_id_function: Callable[[APIRoute], str] |
|
|
|
| DefaultPlaceholder = Default(generate_unique_id), |
|
|
|
strict_content_type: bool | None = None, |
|
|
|
) -> None: |
|
|
|
route_class = route_class_override or self.route_class |
|
|
|
responses = responses or {} |
|
|
|
@ -1105,6 +1135,9 @@ class APIRouter(routing.Router): |
|
|
|
callbacks=current_callbacks, |
|
|
|
openapi_extra=openapi_extra, |
|
|
|
generate_unique_id_function=current_generate_unique_id, |
|
|
|
strict_content_type=strict_content_type |
|
|
|
if strict_content_type is not None |
|
|
|
else self.strict_content_type, |
|
|
|
) |
|
|
|
self.routes.append(route) |
|
|
|
|
|
|
|
@ -1480,6 +1513,7 @@ class APIRouter(routing.Router): |
|
|
|
callbacks=current_callbacks, |
|
|
|
openapi_extra=route.openapi_extra, |
|
|
|
generate_unique_id_function=current_generate_unique_id, |
|
|
|
strict_content_type=route.strict_content_type, |
|
|
|
) |
|
|
|
elif isinstance(route, routing.Route): |
|
|
|
methods = list(route.methods or []) |
|
|
|
|