Replies: 25 comments 2 replies
-
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("/") |
Beta Was this translation helpful? Give feedback.
-
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) |
Beta Was this translation helpful? Give feedback.
-
@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. |
Beta Was this translation helpful? Give feedback.
-
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 |
Beta Was this translation helpful? Give feedback.
-
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 |
Beta Was this translation helpful? Give feedback.
-
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) |
Beta Was this translation helpful? Give feedback.
-
I have the same problem, I cannot render static files in an APIRouter. |
Beta Was this translation helpful? Give feedback.
-
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 |
Beta Was this translation helpful? Give feedback.
-
I tried the same way as lazka and failed. It confuses me a lot that APIRouter has |
Beta Was this translation helpful? Give feedback.
-
the same issue, still can't figure it out, how to serve static files with the router. |
Beta Was this translation helpful? Give feedback.
-
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 |
Beta Was this translation helpful? Give feedback.
-
Hello, same issue here. I use an After that, If I set a non-empty Switching Any workarounds until #1469 (comment) gets addressed? |
Beta Was this translation helpful? Give feedback.
-
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):
|
Beta Was this translation helpful? Give feedback.
-
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 |
Beta Was this translation helpful? Give feedback.
-
any update on this? Running into the same issue. |
Beta Was this translation helpful? Give feedback.
-
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)}") |
Beta Was this translation helpful? Give feedback.
-
so,how to Mounting StaticFiles with an APIRouter???? |
Beta Was this translation helpful? Give feedback.
-
so,how to Mounting StaticFiles with an APIRouter???? who can reply to me ?? |
Beta Was this translation helpful? Give feedback.
-
I also had this issue, solved it by writing another solution instead, that works for serving single page applications: router = APIRouter()
def mount_SPA(router: APIRouter, spa_app_path: Path):
"""Mount a single page application for the given router and path"""
@router.get("/{path:path}")
def ui(path: str):
file = (spa_app_path / path).resolve()
# Make sure no files outside spa_app_path are requested
if file.is_relative_to(spa_app_path) and file.is_file():
return FileResponse(file)
else:
return FileResponse(spa_app_path / 'index.html')
mount_SPA(router, config.react_app_path) |
Beta Was this translation helpful? Give feedback.
-
Well, it cost me a while to figure it out that APIRouter does not support StaticFiles. |
Beta Was this translation helpful? Give feedback.
-
you could also just do a normal route returning a file response and then do whatever you want with it.
(i use it to "hijack" images and run them through a image resizer n stuff and limit what files "can" be returned by static even) |
Beta Was this translation helpful? Give feedback.
-
There's already an issue for this: #10180. |
Beta Was this translation helpful? Give feedback.
-
My current workaround looks like this if anyone is interested: My static files are located in the app's directory at The router for my from fastapi import APIRouter
router = APIRouter(
prefix="/dashboard",
tags=["Dashboard"]
)
... In my from routers import dashboard as DashboardRouter
...
app = FastAPI()
app.include_router(DashboardRouter.router)
app.mount(DashboardRouter.router.prefix + "/static", StaticFiles(directory="static"), name="static")
... Now my static files are reachable at this url, eg: |
Beta Was this translation helpful? Give feedback.
-
I feel like a good solution for this is adding a callback the application can run. so when i include router it calls on_mount |
Beta Was this translation helpful? Give feedback.
-
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.
Beta Was this translation helpful? Give feedback.
All reactions