Including router which has a Mount on "/" does not work
See original GitHub issueFirst Check
- I added a very descriptive title to this issue.
- I used the GitHub search to find a similar issue and didn’t find it.
- I searched the FastAPI documentation, with the integrated search.
- I already searched in Google “How to X in FastAPI” and didn’t find any information.
- I already read and followed all the tutorial in the docs and didn’t find an answer.
- I already checked if it is not related to FastAPI but to Pydantic.
- I already checked if it is not related to FastAPI but to Swagger UI.
- I already checked if it is not related to FastAPI but to ReDoc.
Commit to Help
- I commit to help with one of those options 👆
Example Code
from fastapi.applications import FastAPI
from fastapi.responses import FileResponse
from fastapi.routing import APIRouter
from starlette.routing import Route
frontend_router = APIRouter()
async def index_fallback(request):
return FileResponse("static/index.html")
frontend_router.mount("/", Route("/", index_fallback))
app = FastAPI()
app.include_router(frontend_router)
Description
I expect being able to include a router which has applications mounted at the root.
The problem seems to be that starlette strips any leading slashes from Mount
s such that the resulting path is ""
. For starlette itself this is no problem and it can handle those fine.
When calling include_router
on a FastAPI instance it is however checked that either the prefix starts with (1a) and does not end with (1b) a slash or that every route has a non-empty path (2). The mount with an empty path is in violation of 2. Passing a prefix would solve this issue in most cases however when the router should not have a prefix this is not possible as a prefix of /
would violate 1b.
Operating System
Linux
Operating System Details
Output of uname -a
Linux thinkpad-e570 5.15.13-200.fc35.x86_64 #1 SMP Wed Jan 5 16:39:13 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
FastAPI Version
0.72.0
Python Version
Python 3.10.1
Additional Context
The minimal example is obviously not close to real world usage. My concrete use case is that I would like to mount StaticFiles
to a router which has a few other routes to override some special paths.
Minimized variant of my use case
from fastapi.applications import FastAPI
from fastapi.responses import FileResponse, PlainTextResponse
from fastapi.routing import APIRouter
from fastapi.staticfiles import StaticFiles
from starlette.routing import Route
api_router = APIRouter()
@api_router.get("/users/{username}")
async def user(username: str):
return PlainTextResponse(f"Hello '{username}'")
frontend_router = APIRouter()
async def index_fallback(request):
return FileResponse("static/index.html")
# one example for a path which should not be served by StaticFiles
# in this case html=True could be passed to StaticFiles but in our case there are other paths
# for which this would not work
frontend_router.mount("/", Route("/", index_fallback))
frontend_router.mount("/", StaticFiles(directory="static"), name="static")
app = FastAPI()
app.include_router(api_router, prefix="/api")
app.include_router(frontend_router)
Mounting the StaticFiles
instance to the app works around this issue though it is not an optimal solution.
Furthermore it is possible to archive the same result in starlette
without any problems or workarounds.
Starlette implementation of the above
from starlette.applications import Starlette
from starlette.responses import FileResponse, PlainTextResponse
from starlette.routing import Mount, Route, Router
from starlette.staticfiles import StaticFiles
async def user(request):
return PlainTextResponse(f"Hello '{request.path_params['username']}'")
api_router = Router(
routes=[
Route("/users/{username}", user),
]
)
async def index_fallback(request):
return FileResponse("static/index.html")
frontend_router = Router( # this works without any problems
routes=[
Route("/", index_fallback),
Mount("/", StaticFiles(directory="static"), name="static"),
]
)
app = Starlette( # this also works without any problems
routes=[
Mount("/api", api_router),
Mount("/", frontend_router),
]
)
Issue Analytics
- State:
- Created 2 years ago
- Reactions:1
- Comments:8 (4 by maintainers)
Top GitHub Comments
use
add_api_route
instead ofmount
likefrontend_router.add_api_route('/', index_fallback)
becauseadd_api_route
create Route object internally and mount method not overrided by fastAPI its come from starlette@BilalAlpaslan mounting an ASGI app on root works for me - at least the functional part of the API. In the Swagger UI docs however, I can’t see the operations of the subapplication when mounting on root.