-
-
Notifications
You must be signed in to change notification settings - Fork 6.3k
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mounting StaticFiles with an APIRouter doesn't work #1469
Comments
The I'm not sure it makes sense to mount it on an If there is a specific need to include static files under a router, could you outline why that feature would be more useful then the basic mounting on the app? Could you also point out the specific docs that were confusing so they can be clarified? Finally, if you do need some from typing import Any
from fastapi import FastAPI, Request, APIRouter
from fastapi.staticfiles import StaticFiles
from fastapi.testclient import TestClient
from starlette.routing import Router
app = FastAPI()
router = APIRouter()
@router.get("/")
async def foo(request: Request) -> Any:
# this raises starlette.routing.NoMatchFound
return request.url_for("static", path="/bar")
app.include_router(router)
static_router = Router()
static_router.mount("/", StaticFiles(directory="."), name="static")
app.mount("/static", other_router)
client = TestClient(app)
client.get("/") |
I have different modules with different static things in my app and wanted to separate everything. So that changing the prefix also changes the static prefix. And so they can live in another package not in the same repo.
It's here https://fastapi.tiangolo.com/tutorial/bigger-applications/#path-operations-with-apirouter "All the same options are supported.", but I now see that this only refers to path operations. (btw the search in the docs is broken, it just says "Initializing search" forever) |
@tiangolo we probably need your opinion here on design. Would it be appropriate for @lazka there is an open issue about the search (#1448). It works on some platforms and not on others (not sure that's been narrowed down yet). I have good luck on Chrome on macOS. |
Turns out what I actually want is to mount another FastAPI() like so
This is more in line with flask blueprints. Only downside is the "startup" event isn't triggered for "subapp" :( -> I've filed #1480 |
I just ran into the same issue. I agree that at the very least there should be an error (although I would definitely support mounting within routers). @lazka Good to know that's a solution. Would this introduce any additional latency (because of the duplicated "bookkeeping", FastAPI/starlette does for each ASGI request? Or is that not something to even worry about? Basically, I'm in a similar situation where I have organized my app into around 7 different |
I don't know. I'm still using it that way and hadn't had any issues (besides the events not working, which prevents me from modularization the app) |
I have the same problem, I cannot render static files in an APIRouter. |
Same here. I use @lazka's solution for now, but it would be nice to have this more intuitive. Or at least throw an error if someone tries to mount |
the same issue, still can't figure it out, how to serve static files with the router. |
The underlying problems seems to be that the route does not get applied to the application. The cause is a very restrictive check: This should be expanded to work with |
Hello, same issue here. I use an After that, If I set a non-empty Switching Any workarounds until #1469 (comment) gets addressed? |
You need to refer to your Below is a full working example of this behaviour: from fastapi import APIRouter, FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi.responses import HTMLResponse
app = FastAPI()
app.mount('/static', StaticFiles(directory='static'), name='static')
my_router = APIRouter(prefix='/router')
@my_router.get("/not-working")
async def router_root():
content = "<img src='static/black.png'>"
# -------------------^ note, not absolute path!
return HTMLResponse(content=content)
@my_router.get("/working")
async def router_root():
content = "<img src='/https/github.com/static/black.png'>"
# -------------------^ note, prefixed slash!
return HTMLResponse(content=content)
app.include_router(my_router)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000) Output (first called not-working, then working):
|
As @septatrix has pointed out, the problem lies inside a loop which copies routes from downstream router to an upstream. elif isinstance(route, routing.Mount):
self.mount(prefix + route.path, route.app, route.name) to if-else chain at https://github.com/tiangolo/fastapi/blob/f0388915a8b1cd9f3ae2259bace234ac6249c51a/fastapi/routing.py#L726-L729 |
any update on this? Running into the same issue. |
I find there is not else statement. elif isinstance(route, routing.WebSocketRoute):
self.add_websocket_route(
prefix + route.path, route.endpoint, name=route.name
)
elif isinstance(route, routing.Mount):
self.mount(prefix + route.path, route.app, route.name)
else:
raise Exception(f"Unexcepted route type {type(route)}") |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
Describe the bug
Mounting StaticFiles with an APIRouter doesn't work.
To Reproduce
Expected behavior
I can use APIRouter() as if it was a FastAPI() as noted in the docs.
The text was updated successfully, but these errors were encountered: