FastAPI前端初始目录配置:无需修改HTML解决资源404问题
问题描述
我有一个FastAPI应用,目录结构如下:
app .... main.py .... client ........ index.html ........ index.js ........ style.css ........ cfg ............ index.html ............ index.js ............ style.css ........ components ............ libs ................. etc
main.py 代码:
app = FastAPI() app.mount('/client', StaticFiles(directory='client'), name='static') @app.get('/') async def get(): return FileResponse('client/index.html') @app.get('/cfg') async def cfg(): return FileResponse('client/cfg/index.html')
client/index.html 核心资源引用片段:
<link href="style.css" rel="stylesheet" type="text/css"> <script src="libs/d3/d3.v7.js"></script> <script src="./index.js" type="module" defer></script>
client/cfg/index.html 核心资源引用片段:
<script src="./index.js" type="module" defer></script> <link href="style.css" rel="stylesheet" type="text/css"> <link href="../libs/codemirror-5.65.13/lib/codemirror.css" rel="stylesheet" type="text/css">
当前遇到的问题:
- 访问
localhost:8000时,HTML内的资源请求(如/index.js)返回404,原因是请求路径未包含client前缀 - 尝试过重定向到
client/index.html,资源加载正常但URL变为localhost:8000/client/index.html,不符合需求 - 尝试过将
StaticFiles挂载到根路径,访问cfg页面时,资源会请求根目录的index.js和style.css,而非cfg文件夹下的对应文件,问题未解决 - 无法修改HTML中的路径,需通过FastAPI配置适配
解决方案
方法一:为不同路由挂载独立的StaticFiles实例
为根路径和/cfg路径分别挂载对应目录的静态资源,确保每个页面的资源请求都能匹配到正确的文件位置:
from fastapi import FastAPI from fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse app = FastAPI() # 根路径静态资源映射到client目录 app.mount('/', StaticFiles(directory='client'), name='root_static') # /cfg路径静态资源映射到client/cfg目录 app.mount('/cfg', StaticFiles(directory='client/cfg'), name='cfg_static') @app.get('/') async def get(): return FileResponse('client/index.html') @app.get('/cfg') async def cfg(): return FileResponse('client/cfg/index.html')
效果说明:
- 访问
localhost:8000时,style.css请求路径/style.css会对应client/style.css - 访问
localhost:8000/cfg时,style.css请求路径/cfg/style.css会对应client/cfg/style.css,../libs/xxx会请求/libs/xxx,对应client/libs/xxx,完全匹配HTML中的相对路径
方法二:使用自定义中间件重写请求路径
如果不想挂载多个StaticFiles实例,可以通过中间件自动修改资源请求的路径前缀,适配不同页面的资源位置:
from fastapi import FastAPI, Request from fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse from starlette.middleware.base import BaseHTTPMiddleware app = FastAPI() app.mount('/client', StaticFiles(directory='client'), name='static') @app.get('/') async def get(): return FileResponse('client/index.html') @app.get('/cfg') async def cfg(): return FileResponse('client/cfg/index.html') class RewriteStaticMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): path = request.url.path # 处理根页面的资源请求(排除API、文档路径) if not path.startswith('/cfg') and not path.startswith('/docs') and not path.startswith('/openapi.json'): request.scope['path'] = f'/client{path}' # 处理cfg页面的资源请求(排除文档路径) elif path.startswith('/cfg/') and not path.startswith('/cfg/docs'): new_path = path[4:] request.scope['path'] = f'/client/cfg{new_path}' response = await call_next(request) return response app.add_middleware(RewriteStaticMiddleware)
效果说明:
- 根页面的
/style.css会被重写为/client/style.css - cfg页面的
/cfg/style.css会被重写为/client/cfg/style.css,/cfg/../libs/xxx会被解析为/client/libs/xxx,完全适配HTML中的相对路径写法
内容的提问来源于stack exchange,提问作者maltsevdm
相关产品推荐
相关产品推荐

