From 75e57700309caf9a7788b7e3c8f70ac12643d8ef Mon Sep 17 00:00:00 2001 From: CaioFauza Date: Tue, 12 Oct 2021 19:01:10 -0300 Subject: [PATCH 1/3] Added default error schema to app constructor Co-authored-by: Leonardo Mendes Co-authored-by: Gustavo Braga --- fastapi/applications.py | 4 ++++ fastapi/openapi/models.py | 4 ++++ fastapi/openapi/utils.py | 28 ++++++++++++++++++++++++---- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/fastapi/applications.py b/fastapi/applications.py index 0c25026e2..1181bb233 100644 --- a/fastapi/applications.py +++ b/fastapi/applications.py @@ -15,6 +15,7 @@ from fastapi.openapi.docs import ( get_swagger_ui_html, get_swagger_ui_oauth2_redirect_html, ) +from fastapi.openapi.models import DefaultErrorSchema from fastapi.openapi.utils import get_openapi from fastapi.params import Depends from fastapi.types import DecoratedCallable @@ -62,6 +63,7 @@ class FastAPI(Starlette): root_path: str = "", root_path_in_servers: bool = True, responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None, + default_error_schema: Optional[DefaultErrorSchema] = None, callbacks: Optional[List[BaseRoute]] = None, deprecated: Optional[bool] = None, include_in_schema: bool = True, @@ -123,6 +125,7 @@ class FastAPI(Starlette): self.extra = extra self.dependency_overrides: Dict[Callable[..., Any], Callable[..., Any]] = {} + self.default_error_schema = default_error_schema self.openapi_version = "3.0.2" if self.openapi_url: @@ -141,6 +144,7 @@ class FastAPI(Starlette): terms_of_service=self.terms_of_service, contact=self.contact, license_info=self.license_info, + default_error_schema=self.default_error_schema, routes=self.routes, tags=self.openapi_tags, servers=self.servers, diff --git a/fastapi/openapi/models.py b/fastapi/openapi/models.py index 361c75005..0df0f1a8d 100644 --- a/fastapi/openapi/models.py +++ b/fastapi/openapi/models.py @@ -151,6 +151,10 @@ class Example(BaseModel): class Config: extra = "allow" +class DefaultErrorSchema(BaseModel): + status: int + description: Optional[str] = None + model: BaseModel class ParameterInType(Enum): query = "query" diff --git a/fastapi/openapi/utils.py b/fastapi/openapi/utils.py index 0e73e21bf..a4710064a 100644 --- a/fastapi/openapi/utils.py +++ b/fastapi/openapi/utils.py @@ -29,6 +29,7 @@ from pydantic.schema import ( get_model_name_map, ) from pydantic.utils import lenient_issubclass +from fastapi.openapi.models import DefaultErrorSchema from starlette.responses import JSONResponse from starlette.routing import BaseRoute from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY @@ -167,7 +168,7 @@ def get_openapi_operation_metadata( def get_openapi_path( - *, route: routing.APIRoute, model_name_map: Dict[type, str] + *, route: routing.APIRoute, model_name_map: Dict[type, str], default_error_schema: Optional[DefaultErrorSchema] = None ) -> Tuple[Dict[str, Any], Dict[str, Any], Dict[str, Any]]: path = {} security_schemes: Dict[str, Any] = {} @@ -296,12 +297,30 @@ def get_openapi_path( deep_dict_update(openapi_response, process_response) openapi_response["description"] = description http422 = str(HTTP_422_UNPROCESSABLE_ENTITY) - if (all_route_params or route.body_field) and not any( + if(default_error_schema is not None): + operation["responses"][default_error_schema["status"]] = { + "description": default_error_schema["description"], + "content": { + "application/json": { + "schema": {"$ref": REF_PREFIX + default_error_schema["name"] } + }, + } + } + definitions.update( + { + default_error_schema["name"]: { + "title": default_error_schema["name"], + "type": "object", + "properties": default_error_schema["properties"] + } + } + ) + elif (all_route_params or route.body_field) and not any( [ status in operation["responses"] for status in [http422, "4XX", "default"] ] - ): + ): operation["responses"][http422] = { "description": "Validation Error", "content": { @@ -362,6 +381,7 @@ def get_openapi( openapi_version: str = "3.0.2", description: Optional[str] = None, routes: Sequence[BaseRoute], + default_error_schema: Optional[DefaultErrorSchema] = None, tags: Optional[List[Dict[str, Any]]] = None, servers: Optional[List[Dict[str, Union[str, Any]]]] = None, terms_of_service: Optional[str] = None, @@ -389,7 +409,7 @@ def get_openapi( ) for route in routes: if isinstance(route, routing.APIRoute): - result = get_openapi_path(route=route, model_name_map=model_name_map) + result = get_openapi_path(route=route, model_name_map=model_name_map, default_error_schema=default_error_schema) if result: path, security_schemes, path_definitions = result if path: From 3716cf7464b51fee6250a3d74b554ad50034d2f7 Mon Sep 17 00:00:00 2001 From: CaioFauza Date: Tue, 12 Oct 2021 19:11:16 -0300 Subject: [PATCH 2/3] Added required parameter to default error schema Co-authored-by: Leonardo Mendes Co-authored-by: Gustavo Braga --- fastapi/openapi/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fastapi/openapi/utils.py b/fastapi/openapi/utils.py index a4710064a..5ac534019 100644 --- a/fastapi/openapi/utils.py +++ b/fastapi/openapi/utils.py @@ -311,7 +311,8 @@ def get_openapi_path( default_error_schema["name"]: { "title": default_error_schema["name"], "type": "object", - "properties": default_error_schema["properties"] + "properties": default_error_schema["properties"], + "required": default_error_schema["required"] } } ) From a71bc2cecc8f507dae99fe60a74476176bb20a1b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 10:56:36 +0000 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=8E=A8=20[pre-commit.ci]=20Auto=20for?= =?UTF-8?q?mat=20from=20pre-commit.com=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fastapi/openapi/models.py | 2 ++ fastapi/openapi/utils.py | 12 +++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/fastapi/openapi/models.py b/fastapi/openapi/models.py index 0444aef50..40b26086c 100644 --- a/fastapi/openapi/models.py +++ b/fastapi/openapi/models.py @@ -227,11 +227,13 @@ class Example(TypedDict, total=False): class Config: extra = "allow" + class DefaultErrorSchema(BaseModel): status: int description: Optional[str] = None model: BaseModel + class ParameterInType(Enum): query = "query" header = "header" diff --git a/fastapi/openapi/utils.py b/fastapi/openapi/utils.py index 429809380..2e555e336 100644 --- a/fastapi/openapi/utils.py +++ b/fastapi/openapi/utils.py @@ -419,14 +419,16 @@ def get_openapi_path( openapi_response["description"] = description http422 = str(HTTP_422_UNPROCESSABLE_ENTITY) all_route_params = get_flat_params(route.dependant) - if(default_error_schema is not None): + if default_error_schema is not None: operation["responses"][default_error_schema["status"]] = { "description": default_error_schema["description"], "content": { "application/json": { - "schema": {"$ref": REF_PREFIX + default_error_schema["name"] } + "schema": { + "$ref": REF_PREFIX + default_error_schema["name"] + } }, - } + }, } definitions.update( { @@ -434,7 +436,7 @@ def get_openapi_path( "title": default_error_schema["name"], "type": "object", "properties": default_error_schema["properties"], - "required": default_error_schema["required"] + "required": default_error_schema["required"], } } ) @@ -547,7 +549,7 @@ def get_openapi( model_name_map=model_name_map, field_mapping=field_mapping, separate_input_output_schemas=separate_input_output_schemas, - default_error_schema=default_error_schema + default_error_schema=default_error_schema, ) if result: path, security_schemes, path_definitions = result