Browse Source
- Override APIRouter.mount() to only allow StaticFiles; raise FastAPIError for any other ASGI app with a clear error message - Add APIRouter._mount() as internal method used by include_router() and FastAPI.mount() to bypass the StaticFiles-only restriction - Override FastAPI.mount() to call self.router._mount() directly - Propagate StaticFiles mounts in include_router() combining prefix + router.prefix + route.path correctly Fixes #10180pull/14926/head
3 changed files with 102 additions and 0 deletions
@ -0,0 +1,81 @@ |
|||||
|
"""Tests for mounting StaticFiles under APIRouter (issue #10180).""" |
||||
|
from pathlib import Path |
||||
|
|
||||
|
import pytest |
||||
|
from fastapi import APIRouter, FastAPI |
||||
|
from fastapi.exceptions import FastAPIError |
||||
|
from fastapi.staticfiles import StaticFiles |
||||
|
from fastapi.testclient import TestClient |
||||
|
|
||||
|
|
||||
|
def test_mount_staticfiles_under_router_with_prefix(tmp_path: Path) -> None: |
||||
|
"""StaticFiles mounted on APIRouter with prefix should be accessible.""" |
||||
|
static_file = tmp_path / "hello.txt" |
||||
|
static_file.write_text("hello world") |
||||
|
|
||||
|
app = FastAPI() |
||||
|
api_router = APIRouter(prefix="/api") |
||||
|
|
||||
|
@api_router.get("/app") |
||||
|
def read_main() -> dict: |
||||
|
return {"message": "Hello World from main app"} |
||||
|
|
||||
|
api_router.mount("/static", StaticFiles(directory=tmp_path), name="static") |
||||
|
app.include_router(api_router) |
||||
|
|
||||
|
client = TestClient(app) |
||||
|
|
||||
|
# Regular route should still work |
||||
|
r = client.get("/api/app") |
||||
|
assert r.status_code == 200 |
||||
|
|
||||
|
# StaticFiles should be accessible under router prefix |
||||
|
r = client.get("/api/static/hello.txt") |
||||
|
assert r.status_code == 200 |
||||
|
assert r.text == "hello world" |
||||
|
|
||||
|
|
||||
|
def test_mount_staticfiles_under_router_without_prefix(tmp_path: Path) -> None: |
||||
|
"""StaticFiles mounted on APIRouter without prefix should be accessible.""" |
||||
|
static_file = tmp_path / "test.txt" |
||||
|
static_file.write_text("test content") |
||||
|
|
||||
|
app = FastAPI() |
||||
|
router = APIRouter() |
||||
|
router.mount("/static", StaticFiles(directory=tmp_path), name="static") |
||||
|
app.include_router(router) |
||||
|
|
||||
|
client = TestClient(app) |
||||
|
|
||||
|
r = client.get("/static/test.txt") |
||||
|
assert r.status_code == 200 |
||||
|
assert r.text == "test content" |
||||
|
|
||||
|
|
||||
|
def test_mount_staticfiles_with_include_router_prefix(tmp_path: Path) -> None: |
||||
|
"""include_router prefix + router prefix + mount path should all combine.""" |
||||
|
static_file = tmp_path / "file.txt" |
||||
|
static_file.write_text("combined prefix") |
||||
|
|
||||
|
app = FastAPI() |
||||
|
router = APIRouter(prefix="/api") |
||||
|
router.mount("/static", StaticFiles(directory=tmp_path), name="static") |
||||
|
app.include_router(router, prefix="/v1") |
||||
|
|
||||
|
client = TestClient(app) |
||||
|
|
||||
|
r = client.get("/v1/api/static/file.txt") |
||||
|
assert r.status_code == 200 |
||||
|
assert r.text == "combined prefix" |
||||
|
|
||||
|
|
||||
|
def test_mount_non_staticfiles_app_raises_error() -> None: |
||||
|
"""Mounting a non-StaticFiles ASGI app on APIRouter should raise FastAPIError.""" |
||||
|
router = APIRouter() |
||||
|
sub_app = FastAPI() |
||||
|
|
||||
|
with pytest.raises( |
||||
|
FastAPIError, |
||||
|
match="APIRouter does not support mounting ASGI applications other than StaticFiles.", |
||||
|
): |
||||
|
router.mount("/sub", sub_app) |
||||
Loading…
Reference in new issue