Browse Source

Fix Mount routes being silently dropped in include_router

When a sub-application is mounted on an APIRouter via router.mount() and
the router is later included with app.include_router(), the Mount routes
were silently dropped because include_router() had no handler for
routing.Mount objects. Added an elif branch that creates a new Mount with
the combined prefix and appends it to self.routes.

Closes #10180
pull/15014/head
Anandesh Sharma 5 months ago
parent
commit
165f5355e9
  1. 8
      fastapi/routing.py
  2. 58
      tests/test_mount_sub_app_router.py

8
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:

58
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
Loading…
Cancel
Save