Reputation: 749
If I've got the following static file: static/example.png
and I mount my staticfiles like so:
app.mount('/static', StaticFiles(directory='static'), name='static')
I can now use that file in my HTML as static/example.png
, so long as I only use views that are registered directly to my main app, for example:
@app.get('/home', response_type=HTMLResponse)
def home(request: Request):
return '<img src="static/example.png" ...>'
And this works fine. However, if I create a new APIRouter
, like so:
router = APIRouter(prefix='/dashboards')
app.include_router(router=router)
Now if I try to use static/example.png
in a view that's registered to the new 'dashboards' router, for example like:
@router.get('/home', response_type=HTMLResponse)
def dashboards_home(request: Request):
return '<img src="static/example.png" ...>'
I get the following error:
INFO: 127.0.0.1:52771 - "GET /dashboards/static/example.png HTTP/1.1" 404 Not Found
Basically, it seems that the APIRouter
prefix gets prepended to every url inside my HTML, such as for href or src tag attributes. So instead of being treated it as a local static file, it gets treated as a web URL.
This means that I can't reuse my static files across APIRouter
s without mounting a new StaticFiles
instance against each one, making a copy of the static file under a new folder named after the router prefix, and then adding the corresponding router prefix inside my HTML (in the above example, I'd have to reference the file as dashboards/static/example.png
and make the corresponding file copy).
This then means that I need a copy of each static file for every APIRouter
that needs access to it. Even worse, I can't use jinja2 templates with inheritance features, because depending on where the parent template is being extended from, the static file it references would need different prefixes to be applied to it.
Is there any way I can get all views belonging to an APIRouter
to NOT apply their router's prefix to any urls used inside the HTML they serve up?
Upvotes: 0
Views: 661
Reputation: 5324
UPDATE
You can reference the absolute path by prefixing static
in your html with a /
Below is a fully working example of this:
from fastapi import APIRouter, FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
app = FastAPI()
app.mount('/static', StaticFiles(directory='static'), name='static')
@app.get('/home', response_class=HTMLResponse)
def home(request: Request):
return "<link rel='stylesheet' href='/static/test.css'><h1>hello from home</h1>"
# ---------------------------------------^ absolute path!
router = APIRouter(prefix='/myrouter')
@router.get('/route', response_class=HTMLResponse)
def route(request: Request):
return "<link rel='stylesheet' href='/static/test.css'><h1>hello from home</h1>"
# ---------------------------------------^ absolute path!
app.include_router(router)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
OLD ANSWER
You can use relative pointers. The below is a full example, it should run as-is:
from fastapi import APIRouter, FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
app = FastAPI()
app.mount('/static', StaticFiles(directory='static'), name='static')
@app.get('/home', response_class=HTMLResponse)
def home(request: Request):
return "<link rel='stylesheet' href='static/test.css'><h1>hello from home</h1>"
router = APIRouter(prefix='/myrouter')
@router.get('/route', response_class=HTMLResponse)
def route(request: Request):
return "<link rel='stylesheet' href='../static/test.css'><h1>hello from home</h1>"
# ---------------------------------------^ relative path!
app.include_router(router)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
This way, you can just have one StaticFiles
declaration.
Upvotes: 1