diff --git a/docs/en/docs/how-to/custom-request-and-route.md b/docs/en/docs/how-to/custom-request-and-route.md index 8e1f353cd..66d8ac4f3 100644 --- a/docs/en/docs/how-to/custom-request-and-route.md +++ b/docs/en/docs/how-to/custom-request-and-route.md @@ -102,8 +102,8 @@ If an exception occurs, the`Request` instance will still be in scope, so we can You can also set the `route_class` parameter of an `APIRouter`: -{* ../../docs_src/custom_request_and_route/tutorial003.py hl[26] *} +{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[26] *} In this example, the *path operations* under the `router` will use the custom `TimedRoute` class, and will have an extra `X-Response-Time` header in the response with the time it took to generate the response: -{* ../../docs_src/custom_request_and_route/tutorial003.py hl[13:20] *} +{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[13:20] *} diff --git a/docs_src/custom_request_and_route/tutorial003_py310.py b/docs_src/custom_request_and_route/tutorial003_py310.py new file mode 100644 index 000000000..f4e60be61 --- /dev/null +++ b/docs_src/custom_request_and_route/tutorial003_py310.py @@ -0,0 +1,39 @@ +import time +from collections.abc import Callable + +from fastapi import APIRouter, FastAPI, Request, Response +from fastapi.routing import APIRoute + + +class TimedRoute(APIRoute): + def get_route_handler(self) -> Callable: + original_route_handler = super().get_route_handler() + + async def custom_route_handler(request: Request) -> Response: + before = time.time() + response: Response = await original_route_handler(request) + duration = time.time() - before + response.headers["X-Response-Time"] = str(duration) + print(f"route duration: {duration}") + print(f"route response: {response}") + print(f"route response headers: {response.headers}") + return response + + return custom_route_handler + + +app = FastAPI() +router = APIRouter(route_class=TimedRoute) + + +@app.get("/") +async def not_timed(): + return {"message": "Not timed"} + + +@router.get("/timed") +async def timed(): + return {"message": "It's the time of my life"} + + +app.include_router(router) diff --git a/tests/test_tutorial/test_custom_request_and_route/test_tutorial003.py b/tests/test_tutorial/test_custom_request_and_route/test_tutorial003.py index db5dad7cf..9e895b2da 100644 --- a/tests/test_tutorial/test_custom_request_and_route/test_tutorial003.py +++ b/tests/test_tutorial/test_custom_request_and_route/test_tutorial003.py @@ -1,17 +1,32 @@ +import importlib + +import pytest from fastapi.testclient import TestClient -from docs_src.custom_request_and_route.tutorial003 import app - -client = TestClient(app) +from tests.utils import needs_py310 -def test_get(): +@pytest.fixture( + name="client", + params=[ + pytest.param("tutorial003"), + pytest.param("tutorial003_py310", marks=needs_py310), + ], +) +def get_client(request: pytest.FixtureRequest): + mod = importlib.import_module(f"docs_src.custom_request_and_route.{request.param}") + + client = TestClient(mod.app) + return client + + +def test_get(client: TestClient): response = client.get("/") assert response.json() == {"message": "Not timed"} assert "X-Response-Time" not in response.headers -def test_get_timed(): +def test_get_timed(client: TestClient): response = client.get("/timed") assert response.json() == {"message": "It's the time of my life"} assert "X-Response-Time" in response.headers