From 69230e83befaa8c7b35724acab47199f84238bfd Mon Sep 17 00:00:00 2001 From: Christian Tanul Date: Sun, 14 Sep 2025 11:08:44 +0300 Subject: [PATCH 1/2] Fix compatibility with apps using custom default_response_class Explicitly set response_class=JSONResponse for debug toolbar API route to prevent conflicts when FastAPI apps override default_response_class (e.g., to HTMLResponse). Without this, the toolbar returns JSON data but inherits HTMLResponse, causing encoding errors. --- debug_toolbar/middleware.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/debug_toolbar/middleware.py b/debug_toolbar/middleware.py index 0f05337..66ba0e7 100644 --- a/debug_toolbar/middleware.py +++ b/debug_toolbar/middleware.py @@ -10,6 +10,7 @@ from fastapi.responses import StreamingResponse from fastapi.staticfiles import StaticFiles from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint +from starlette.responses import JSONResponse from starlette.routing import NoMatchFound from starlette.types import ASGIApp @@ -44,6 +45,7 @@ def init_toolbar(self) -> None: self.settings.API_URL, name="debug_toolbar.render_panel", include_in_schema=False, + response_class=JSONResponse, )(self.require_show_toolbar(render_panel)) self.router.mount( From 46b12eba812557edf268d917579baed4bcd89759 Mon Sep 17 00:00:00 2001 From: Christian Tanul Date: Wed, 22 Jul 2026 09:34:12 +0200 Subject: [PATCH 2/2] Support FastAPI included routers --- debug_toolbar/utils.py | 13 +++++++++---- tests/test_middleware.py | 15 ++++++++++++++- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/debug_toolbar/utils.py b/debug_toolbar/utils.py index 2c838f6..4a3363f 100644 --- a/debug_toolbar/utils.py +++ b/debug_toolbar/utils.py @@ -44,10 +44,15 @@ def matched_route(request: Request) -> APIRoute | None: for route in request.app.routes: match, _ = route.matches(request.scope) - if match == Match.FULL: - if hasattr(route, "endpoint"): - return route - break + if match != Match.FULL: + continue + if hasattr(route, "endpoint"): + return route + if contexts := getattr(route, "effective_route_contexts", None): + for context in contexts(): + match, _ = context.matches(request.scope) + if match == Match.FULL and hasattr(context.original_route, "endpoint"): + return context.original_route return None diff --git a/tests/test_middleware.py b/tests/test_middleware.py index f107890..ff41546 100644 --- a/tests/test_middleware.py +++ b/tests/test_middleware.py @@ -1,4 +1,5 @@ -from fastapi import FastAPI, Request, status +from fastapi import APIRouter, FastAPI, Request, status +from fastapi.responses import HTMLResponse from debug_toolbar.middleware import show_toolbar from debug_toolbar.settings import DebugToolbarSettings @@ -18,6 +19,18 @@ def test_json(client: TestClient) -> None: assert client.get_store_id("/openapi.json") +def test_included_router(app: FastAPI) -> None: + router = APIRouter() + + @router.get("/included", response_class=HTMLResponse) + async def included() -> str: + return "Included" + + app.include_router(router) + + assert TestClient(app).get_store_id("/included") + + def test_404(client: TestClient) -> None: response = client.get("/404") assert response.status_code == status.HTTP_404_NOT_FOUND