matthewgdv
matthewgdv

Reputation: 749

How to reuse static files across multiple APIRouters?

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 APIRouters 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

Answers (1)

JarroVGIT
JarroVGIT

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

Related Questions