diff --git a/fastapi/routing.py b/fastapi/routing.py index d17650a627..fbf580b86c 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -1548,6 +1548,14 @@ class APIRouter(routing.Router): self.add_websocket_route( prefix + route.path, route.endpoint, name=route.name ) + elif isinstance(route, Mount): + self.routes.append( + Mount( + prefix + route.path, + app=route.app, + name=route.name, + ) + ) for handler in router.on_startup: self.add_event_handler("startup", handler) for handler in router.on_shutdown: diff --git a/tests/test_mount_sub_app_router.py b/tests/test_mount_sub_app_router.py new file mode 100644 index 0000000000..9d81703318 --- /dev/null +++ b/tests/test_mount_sub_app_router.py @@ -0,0 +1,58 @@ +from fastapi import APIRouter, FastAPI +from fastapi.testclient import TestClient +from starlette.routing import Mount + + +def test_sub_app_mounted_on_router_is_accessible(): + """Sub-apps mounted on an APIRouter should be reachable after include_router.""" + sub_app = FastAPI() + + @sub_app.get("/status") + def sub_status(): + return {"status": "ok"} + + router = APIRouter() + router.mount("/sub", sub_app) + + app = FastAPI() + app.include_router(router) + + client = TestClient(app) + response = client.get("/sub/status") + assert response.status_code == 200 + assert response.json() == {"status": "ok"} + + +def test_sub_app_with_router_prefix(): + """The include_router prefix should be prepended to the mount path.""" + sub_app = FastAPI() + + @sub_app.get("/info") + def sub_info(): + return {"info": "value"} + + router = APIRouter() + router.mount("/sub", sub_app) + + app = FastAPI() + app.include_router(router, prefix="/api") + + client = TestClient(app) + response = client.get("/api/sub/info") + assert response.status_code == 200 + assert response.json() == {"info": "value"} + + +def test_mount_preserved_in_routes(): + """Verify the Mount object ends up in app.routes.""" + sub_app = FastAPI() + router = APIRouter() + router.mount("/sub", sub_app) + + app = FastAPI() + app.include_router(router) + + mount_routes = [ + r for r in app.routes if isinstance(r, Mount) and "/sub" in r.path + ] + assert len(mount_routes) == 1