Fix included router rate limitingfix: handle included routers in default rate limiting - #282
Conversation
|
@15054538509 The added test uses limiter = Limiter(key_func=lambda request: request.client.host, default_limits=["1/minute"])
app = FastAPI()
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
app.add_middleware(SlowAPIMiddleware) # same result with SlowAPIASGIMiddleware
router = APIRouter()
@router.get("/t1")
async def endpoint(request: Request):
return {"ok": True}
app.include_router(router, prefix="/api")
with TestClient(app) as client:
assert [client.get("/api/t1").status_code for _ in range(2)] == [200, 429]Actual result on this PR: A two-level inner = APIRouter(prefix="/inner")
# add GET /t1 to inner
outer = APIRouter(prefix="/outer")
outer.include_router(inner)
app.include_router(outer, prefix="/api")
# GET /api/outer/inner/t1 twice -> [200, 200]The reason is that FastAPI 0.137's A minimal change that I verified locally is to prefer the effective candidates before falling back to the existing generic route/original-router lookup: def _get_nested_routes(route: BaseRoute) -> Optional[Iterable[BaseRoute]]:
effective_candidates = getattr(route, "effective_candidates", None)
if callable(effective_candidates):
return effective_candidates()
nested_routes = getattr(route, "routes", None)
if nested_routes is not None:
return nested_routes
original_router = getattr(route, "original_router", None)
return getattr(original_router, "routes", None)With that change, I got
There is also a test-environment gap: this repository's current |
Fix default rate limiting for FastAPI included routers.
Root cause: FastAPI 0.137+ stores
include_router()entries as_IncludedRouterproxies, soapp.routesno longer exposes a direct.endpointfor nested routes. slowapi was treating those routes as unmatched and skipping default limits.Changes:
default_limitsValidation:
pytest tests/test_fastapi_extension.py -q