]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
✨ Allow `Response` type hint as dependency annotation (#14794)
authorJonathan Fulton <jonathan.s.fulton@gmail.com>
Thu, 5 Feb 2026 18:23:16 +0000 (13:23 -0500)
committerGitHub <noreply@github.com>
Thu, 5 Feb 2026 18:23:16 +0000 (18:23 +0000)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Motov Yurii <109919500+YuriiMotov@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
fastapi/dependencies/utils.py
tests/test_response_dependency.py [new file with mode: 0644]

index fc5dfed85a1036211cef93641c44f4c8297eeac5..3037b233b9ca1af031cf7e3d6f3acf28ac3961fe 100644 (file)
@@ -449,7 +449,9 @@ def analyze_param(
         depends = dataclasses.replace(depends, dependency=type_annotation)
 
     # Handle non-param type annotations like Request
-    if lenient_issubclass(
+    # Only apply special handling when there's no explicit Depends - if there's a Depends,
+    # the dependency will be called and its return value used instead of the special injection
+    if depends is None and lenient_issubclass(
         type_annotation,
         (
             Request,
@@ -460,7 +462,6 @@ def analyze_param(
             SecurityScopes,
         ),
     ):
-        assert depends is None, f"Cannot specify `Depends` for type {type_annotation!r}"
         assert field_info is None, (
             f"Cannot specify FastAPI annotation for type {type_annotation!r}"
         )
diff --git a/tests/test_response_dependency.py b/tests/test_response_dependency.py
new file mode 100644 (file)
index 0000000..38c3595
--- /dev/null
@@ -0,0 +1,173 @@
+"""Test using special types (Response, Request, BackgroundTasks) as dependency annotations.
+
+These tests verify that special FastAPI types can be used with Depends() annotations
+and that the dependency injection system properly handles them.
+"""
+
+from typing import Annotated
+
+from fastapi import BackgroundTasks, Depends, FastAPI, Request, Response
+from fastapi.responses import JSONResponse
+from fastapi.testclient import TestClient
+
+
+def test_response_with_depends_annotated():
+    """Response type hint should work with Annotated[Response, Depends(...)]."""
+    app = FastAPI()
+
+    def modify_response(response: Response) -> Response:
+        response.headers["X-Custom"] = "modified"
+        return response
+
+    @app.get("/")
+    def endpoint(response: Annotated[Response, Depends(modify_response)]):
+        return {"status": "ok"}
+
+    client = TestClient(app)
+    resp = client.get("/")
+
+    assert resp.status_code == 200
+    assert resp.json() == {"status": "ok"}
+    assert resp.headers.get("X-Custom") == "modified"
+
+
+def test_response_with_depends_default():
+    """Response type hint should work with Response = Depends(...)."""
+    app = FastAPI()
+
+    def modify_response(response: Response) -> Response:
+        response.headers["X-Custom"] = "modified"
+        return response
+
+    @app.get("/")
+    def endpoint(response: Response = Depends(modify_response)):
+        return {"status": "ok"}
+
+    client = TestClient(app)
+    resp = client.get("/")
+
+    assert resp.status_code == 200
+    assert resp.json() == {"status": "ok"}
+    assert resp.headers.get("X-Custom") == "modified"
+
+
+def test_response_without_depends():
+    """Regular Response injection should still work."""
+    app = FastAPI()
+
+    @app.get("/")
+    def endpoint(response: Response):
+        response.headers["X-Direct"] = "set"
+        return {"status": "ok"}
+
+    client = TestClient(app)
+    resp = client.get("/")
+
+    assert resp.status_code == 200
+    assert resp.json() == {"status": "ok"}
+    assert resp.headers.get("X-Direct") == "set"
+
+
+def test_response_dependency_chain():
+    """Response dependency should work in a chain of dependencies."""
+    app = FastAPI()
+
+    def first_modifier(response: Response) -> Response:
+        response.headers["X-First"] = "1"
+        return response
+
+    def second_modifier(
+        response: Annotated[Response, Depends(first_modifier)],
+    ) -> Response:
+        response.headers["X-Second"] = "2"
+        return response
+
+    @app.get("/")
+    def endpoint(response: Annotated[Response, Depends(second_modifier)]):
+        return {"status": "ok"}
+
+    client = TestClient(app)
+    resp = client.get("/")
+
+    assert resp.status_code == 200
+    assert resp.headers.get("X-First") == "1"
+    assert resp.headers.get("X-Second") == "2"
+
+
+def test_response_dependency_returns_different_response_instance():
+    """Dependency that returns a different Response instance should work.
+
+    When a dependency returns a new Response object (e.g., JSONResponse) instead
+    of modifying the injected one, the returned response should be used and any
+    modifications to it in the endpoint should be preserved.
+    """
+    app = FastAPI()
+
+    def default_response() -> Response:
+        response = JSONResponse(content={"status": "ok"})
+        response.headers["X-Custom"] = "initial"
+        return response
+
+    @app.get("/")
+    def endpoint(response: Annotated[Response, Depends(default_response)]):
+        response.headers["X-Custom"] = "modified"
+        return response
+
+    client = TestClient(app)
+    resp = client.get("/")
+
+    assert resp.status_code == 200
+    assert resp.json() == {"status": "ok"}
+    assert resp.headers.get("X-Custom") == "modified"
+
+
+# Tests for Request type hint with Depends
+def test_request_with_depends_annotated():
+    """Request type hint should work in dependency chain."""
+    app = FastAPI()
+
+    def extract_request_info(request: Request) -> dict:
+        return {
+            "path": request.url.path,
+            "user_agent": request.headers.get("user-agent", "unknown"),
+        }
+
+    @app.get("/")
+    def endpoint(
+        info: Annotated[dict, Depends(extract_request_info)],
+    ):
+        return info
+
+    client = TestClient(app)
+    resp = client.get("/", headers={"user-agent": "test-agent"})
+
+    assert resp.status_code == 200
+    assert resp.json() == {"path": "/", "user_agent": "test-agent"}
+
+
+# Tests for BackgroundTasks type hint with Depends
+def test_background_tasks_with_depends_annotated():
+    """BackgroundTasks type hint should work with Annotated[BackgroundTasks, Depends(...)]."""
+    app = FastAPI()
+    task_results = []
+
+    def background_task(message: str):
+        task_results.append(message)
+
+    def add_background_task(background_tasks: BackgroundTasks) -> BackgroundTasks:
+        background_tasks.add_task(background_task, "from dependency")
+        return background_tasks
+
+    @app.get("/")
+    def endpoint(
+        background_tasks: Annotated[BackgroundTasks, Depends(add_background_task)],
+    ):
+        background_tasks.add_task(background_task, "from endpoint")
+        return {"status": "ok"}
+
+    client = TestClient(app)
+    resp = client.get("/")
+
+    assert resp.status_code == 200
+    assert "from dependency" in task_results
+    assert "from endpoint" in task_results