diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index e6700992c6..e49ad801ca 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -72,6 +72,7 @@ from starlette.datastructures import ( QueryParams, UploadFile, ) +from starlette.exceptions import HTTPException from starlette.requests import HTTPConnection, Request from starlette.responses import Response from starlette.websockets import WebSocket @@ -700,20 +701,15 @@ async def solve_dependencies( and sub_dependant.param_annotation is not inspect.Parameter.empty and not _annotation_allows_none(sub_dependant.param_annotation) ): - errors.append( - { - "type": "missing", - "loc": ("dependency", sub_dependant.name), - "msg": ( - f"Dependency returned None for parameter " - f"'{sub_dependant.name}' which is annotated as " - f"non-optional. Use 'Optional[...]' or '... | None' " - f"if the dependency can return None (e.g. when using " - f"auto_error=False)." - ), - } + raise HTTPException( + status_code=500, + detail=( + f"Security dependency returned None for parameter" + f" '{sub_dependant.name}' which is annotated as" + f" non-optional. Use 'Optional[...]' or '... | None'" + f" when the security scheme has auto_error=False." + ), ) - continue values[sub_dependant.name] = solved if sub_dependant.cache_key not in dependency_cache: dependency_cache[sub_dependant.cache_key] = solved diff --git a/tests/test_security_auto_error_none_type_safety.py b/tests/test_security_auto_error_none_type_safety.py index e4b11d0642..0d3d8b303d 100644 --- a/tests/test_security_auto_error_none_type_safety.py +++ b/tests/test_security_auto_error_none_type_safety.py @@ -12,7 +12,6 @@ from fastapi.security import ( OAuth2PasswordBearer, ) from fastapi.testclient import TestClient -from inline_snapshot import snapshot def test_oauth2_non_optional_no_auth(): @@ -23,20 +22,10 @@ def test_oauth2_non_optional_no_auth(): def get_me(token: str = Depends(oauth2)): return {"token": token} - client = TestClient(app) + client = TestClient(app, raise_server_exceptions=False) resp = client.get("/me") - assert resp.status_code == 422 - assert resp.json() == snapshot( - { - "detail": [ - { - "type": "missing", - "loc": ["dependency", "token"], - "msg": "Dependency returned None for parameter 'token' which is annotated as non-optional. Use 'Optional[...]' or '... | None' if the dependency can return None (e.g. when using auto_error=False).", - } - ] - } - ) + assert resp.status_code == 500 + assert "non-optional" in resp.json()["detail"] def test_oauth2_optional_no_auth(): @@ -75,9 +64,9 @@ def test_http_bearer_non_optional_no_auth(): def get_profile(creds: HTTPAuthorizationCredentials = Depends(bearer)): return {"scheme": creds.scheme} - client = TestClient(app) + client = TestClient(app, raise_server_exceptions=False) resp = client.get("/profile") - assert resp.status_code == 422 + assert resp.status_code == 500 def test_http_bearer_optional_no_auth(): @@ -104,9 +93,9 @@ def test_api_key_header_non_optional_no_key(): def get_data(key: str = Depends(api_key)): return {"key": key} - client = TestClient(app) + client = TestClient(app, raise_server_exceptions=False) resp = client.get("/data") - assert resp.status_code == 422 + assert resp.status_code == 500 def test_api_key_query_non_optional_no_key(): @@ -117,9 +106,9 @@ def test_api_key_query_non_optional_no_key(): def get_data(key: str = Depends(api_key)): return {"key": key} - client = TestClient(app) + client = TestClient(app, raise_server_exceptions=False) resp = client.get("/data") - assert resp.status_code == 422 + assert resp.status_code == 500 def test_api_key_cookie_non_optional_no_key(): @@ -130,9 +119,9 @@ def test_api_key_cookie_non_optional_no_key(): def get_data(key: str = Depends(api_key)): return {"key": key} - client = TestClient(app) + client = TestClient(app, raise_server_exceptions=False) resp = client.get("/data") - assert resp.status_code == 422 + assert resp.status_code == 500 def test_http_basic_non_optional_no_auth(): @@ -143,9 +132,9 @@ def test_http_basic_non_optional_no_auth(): def get_data(creds: HTTPBasicCredentials = Depends(basic)): return {"user": creds.username} - client = TestClient(app) + client = TestClient(app, raise_server_exceptions=False) resp = client.get("/data") - assert resp.status_code == 422 + assert resp.status_code == 500 def test_annotated_syntax_non_optional(): @@ -156,9 +145,9 @@ def test_annotated_syntax_non_optional(): def get_data(key: Annotated[str, Depends(api_key)]): return {"key": key} - client = TestClient(app) + client = TestClient(app, raise_server_exceptions=False) resp = client.get("/data") - assert resp.status_code == 422 + assert resp.status_code == 500 resp2 = client.get("/data", headers={"X-API-Key": "secret"}) assert resp2.status_code == 200 @@ -221,9 +210,9 @@ def test_annotated_non_optional_inner_blocked(): def get_data(key: Annotated[str, Depends(api_key)]): return {"key": key} - client = TestClient(app) + client = TestClient(app, raise_server_exceptions=False) resp = client.get("/data") - assert resp.status_code == 422 + assert resp.status_code == 500 def test_annotated_optional_inner_allowed():