最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

html - Serving React app statically from FastAPI - Stack Overflow

programmeradmin7浏览0评论

I have a React app that I'm trying to serve statically from FastAPI. I've got all my React build artifacts in my static folder. So, my FastAPI app looks like this:

static/
    assets/
        index-lATvXaZG.js
    index.html
app.py

In app.py, I have:

app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="react")

@app.get("")
@app.get("/")
@app.get("/index.html")
def serve_index():
    return FileResponse("static/index.html")

However, when index.html is loaded, the client understandably makes a GET request for /assets/index-lATvXaZG.js. Of course, FastAPI can't find this.

Is there a setting I can put in my React app (or, perhaps, in my FastAPI setup) to get the paths lined up?

I have a React app that I'm trying to serve statically from FastAPI. I've got all my React build artifacts in my static folder. So, my FastAPI app looks like this:

static/
    assets/
        index-lATvXaZG.js
    index.html
app.py

In app.py, I have:

app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="react")

@app.get("")
@app.get("/")
@app.get("/index.html")
def serve_index():
    return FileResponse("static/index.html")

However, when index.html is loaded, the client understandably makes a GET request for /assets/index-lATvXaZG.js. Of course, FastAPI can't find this.

Is there a setting I can put in my React app (or, perhaps, in my FastAPI setup) to get the paths lined up?

Share Improve this question edited Mar 24 at 18:04 Chris 35.5k10 gold badges104 silver badges250 bronze badges asked Mar 24 at 15:25 Daniel WalkerDaniel Walker 6,8027 gold badges24 silver badges62 bronze badges 0
Add a comment  | 

1 Answer 1

Reset to default 1

I should start by highly recommending having a look at this answer and this answer, as the answer posted here is essentially based on what is described in those answers.

Option 1 (recommended)

The issue should easily be solved by properly using the StaticFiles instance for such purposes. This means that you should set the html flag to True and serve the static files at / route. That way, you could access the index.html by simply typing in the address bar of the browser, for instance, http://127.0.0.1:8000/ or http://localhost:8000/. Hence, any references to /assets/whatever should work just fine.

Example

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles

app = FastAPI()

app.mount('/', StaticFiles(directory='static', html=True), name='static')

Keep your files structure as is:

static/
    assets/
        index.js
    index.html
app.py

Option 2

If you would like to keep your current implementation (that I wouldn't recommend)—meaning that you mount the StaticFiles instance to /static path, i.e., app.mount("/static", StaticFiles(directory="static"), name="static") and keep the html flag set to False (which is the default setting)—you could simply add the following line at the top of every HTML file in the static directory, which essentially specifies the <base> URL to use for all relative URLs in a document:

<base href="static/">

That way, the prefix static/ should automatically be added to any reference that is made inside the HTML file(s) and solve the issue (the static/ missing from the start of the reference is essentially the reason for not finding the assets, as you serve them under the /static route—app.mount("/static",...).

Option 3

Alternatively, if you didn't want to add <base href="static/"> to every file, you could have a custom 404 exception_handler, as demonstrated here, and inside of it get the URL path from the Request object, as shown here, and then append static/ to the front of the path and return a FileResponse (if the file exists). Example:

from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import FileResponse, PlainTextResponse
from fastapi.staticfiles import StaticFiles
import os


# Same implementation as in your question
app = FastAPI()
app.mount("/static", ...

@app.get("")
...


# The 404 exception handler
@app.exception_handler(404)
async def not_found_exception_handler(request: Request, exc: HTTPException):
    filepath = os.path.join('static/', request.url.path[1:])
    if os.path.isfile(filepath):
        return FileResponse(filepath)
    else:
        return PlainTextResponse("404 Not Found",  status_code=404)

Instead of the custom 404 exception_handler, one could use an endpoint to serve such asset files (similar to this), by capturing the URL path, as explained here, and appending static/assets/ to the front of it. Hence, any files lying under the assets/ directory, such as assets/index.js or assets/images/img.png, would be successfully served.

@app.get("/assets/{asset:path}")
async def get_asset(asset: str):
    filepath = os.path.join('static/assets/', asset)
    if os.path.isfile(filepath):
        return FileResponse(filepath)
    else:
        return PlainTextResponse("404 Not Found",  status_code=404)

Again, this is an approach that I wouldn't really recommend following, and with which one should be very careful, as one could maliciously try to take advantage of it and attempt to access likely restricted files that they are not authorized to do so—that's why os.path.join('static/', ... is used in the examples above, in order to mitigate such risks and only lookup for files under the static/ directory.

Choosing Option 1 described earlier should be the prefered approach for such purposes.

发布评论

评论列表(0)

  1. 暂无评论