使用 StaticFiles 时如何在 FastAPI 根路径中加载与 index.html 不同的文件?

Abb*_*bas 4 python static-files starlette fastapi fileresponse

这是一个简单的静态 FastAPI 应用程序。通过此设置,即使根路径预计返回 a FileResponsecustom.html应用程序仍会返回index.html。如何让根路径工作并渲染custom.html

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse

app = FastAPI()


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

@app.get("/")
async def index() -> FileResponse:
    return FileResponse("custom.html", media_type="html")
Run Code Online (Sandbox Code Playgroud)

Chr*_*ris 8

根据 Starlette文档

\n
\n

静态文件

\n

签名:StaticFiles(directory=None, packages=None, check_dir=True)

\n
    \n
  • html- 以 HTML 模式运行。index.html如果此类文件存在,则自动加载目录。
  • \n
\n
\n

另外,如您提供的代码片段所示,您已挂载StaticFiles到根目录(即/),而不是例如/static(或其他路径名),如下所示:

\n
from fastapi import FastAPI\nfrom fastapi.staticfiles import StaticFiles\n\napp = FastAPI()\n\napp.mount(\'/static\', StaticFiles(directory=\'static\'), name=\'static\')\n
Run Code Online (Sandbox Code Playgroud)\n

根据 FastAPI文档

\n
\n

“安装”意味着在特定路径中添加完整的“独立”应用程序,然后负责处理所有子路径

\n
\n

因此,任何以 开头的路径都/将由该应用程序处理StaticFiles,并且由于html=True在参数中指定,因此index.html将自动加载;无论创建一个指向根路径的单独端点/并尝试返回其他内容,如问题中给出的示例所示。

\n

订单事宜

\n

例如,如果您在定义端点app.mount("/",StaticFiles(...移动了行,您会发现顺序很重要,并且不会自动加载,因为端点是按 order 评估的。请注意,在您的情况下,您可能会得到一个,因为您的端点将被调用并尝试查找,但如果该文件不是位于根目录下,而是位于目录下(如代码所示),那么您将出现错误,因此您应该返回.@app.get("/")index.htmlInternal Server Error@app.get("/")custom.html//staticFile does not existFileResponse(\'static/custom.html\')

\n

即使您删除了html=True,但保留StaticFiles安装到根目录并在端点之前定义,在尝试访问 时/也会收到错误响应。这是因为路由仍然由应用程序处理(如前所述),因此您应该需要指定您想要访问的文件(当不使用时),例如. 即使您在代码中定义了其他端点(例如,,,),只要挂载到根目录(即,)并在代码中定义在所有其他端点之前,例如:{"detail":"Not Found"}http://localhost:8000//StaticFileshtml=Truehttp://localhost:8000/index.html/register/login/helloStaticFiles/

\n
app.mount(\'/\', StaticFiles(directory=\'static\'), name=\'static\')\n\n@app.post(\'/register\')\nasync def register():\n    pass\n\n@app.post(\'/login\')\nasync def login():\n    pass\n\n@app.get(\'/hello\')\nasync def hello():\n    pass\n
Run Code Online (Sandbox Code Playgroud)\n

对这些路由的每个请求都将再次由应用程序处理StaticFiles,因此会导致错误响应,例如{"detail":"Not Found"}(如果您发送GET请求,例如当您在浏览器的地址栏中键入 URL,然后点击Enter键,并且给定路径与 Web 目录中的文件名不匹配static),或者{detail": "Method Not Allowed"}(如果您POST通过 Swagger UI 或某些其他客户端平台/应用程序发出请求)。正如Starlette 的文档StaticFiles中所述(另请参阅StaticFiles类实现):

\n
\n

静态文件将响应404 Not found或响应不匹配405 Method not allowed的请求。在 HTML 模式下,如果\n404.html存在,则会显示 404 响应。

\n
\n

因此,您应该将StaticFiles实例安装到不同/唯一的路径,例如/static(即,app.mount(\'/static\', ...如本答案顶部所示),或者,如果您仍然希望将StaticFiles实例安装到/路径,请StaticFiles 在之后定义在声明所有 API进行定义端点,例如:

\n
from fastapi import FastAPI\nfrom fastapi.staticfiles import StaticFiles\nfrom fastapi.responses import FileResponse\n\napp = FastAPI()\n\n\n@app.post(\'/register\')\nasync def register():\n    pass\n\n\n@app.post(\'/login\')\nasync def login():\n    pass\n\n\n@app.get(\'/hello\')\nasync def hello():\n    pass\n\n\n@app.get(\'/\')\nasync def index():\n    return FileResponse(\'static/custom.html\')\n \n \napp.mount(\'/\',StaticFiles(directory=\'static\', html=True), name=\'static\')\n
Run Code Online (Sandbox Code Playgroud)\n

笔记

\n

每次加载网页时,浏览器都会缓存页面上的大部分内容,以缩短加载时间(用户下次加载页面时)。因此,如果您尝试了前面提供的示例,即应用程序在每个 API 端点之前StaticFiles定义,然后使用相同的浏览器会话,您尝试了上面的示例,其中应用程序在所有 API 端点之后定义,但浏览器在浏览器中访问\xe2\x80\x94时,仍然显示文件内容而不是\xe2\x80\x94,这是由于浏览器从缓存加载网页所致。为了解决这个问题,您可以清除浏览器的缓存,或者在隐身窗口中打开网页(完成后将其关闭),或者只需在浏览器中按+而不是仅按(使用隐身或常规窗口),这将强制浏览器从服务器检索网页,而不是从缓存加载网页。StaticFilesstatic/index.htmlstatic/custom.htmlhttp://localhost:8000/CtrlF5F5

\n

关于 FastAPI 中端点的顺序,您可能还会发现这个答案很有帮助。

\n

选项html=True

\n

html将实例参数设置StaticFilesTrue(即html=True)只是提供了一种仅用一行代码即可提供 Web 内容目录的简单方法。如果您需要提供静态文件,例如包文档目录,那么这就是正确的方法。但是,如果您需要提供动态更新的不同 HTML 文件,并且希望创建其他路由/端点,那么您最好看看Templates而不是 FileResponse),以及将StaticFiles实例挂载到不同的路径(例如,/static),而不是根路径(并且不使用html=True)。

\n