这里有一个简单的静态FastAPI应用程序。使用此设置,即使根路径预期返回custom.html
的FileResponse
,应用程序仍然返回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")
根据Starlette文档:
静态文件
签名:
StaticFiles(directory=None, packages=None, check_dir=True)
html
-在HTML模式下运行。如果存在目录的index.html
,则自动加载该文件
此外,如您提供的代码片段所示,您已将StaticFiles
安装到根目录(即/
),而不是例如/static
(或其他路径名),如下所示:
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
app = FastAPI()
app.mount('/static', StaticFiles(directory='static'), name='static')
根据FastAPI文档:
"安装";意味着添加一个完整的"strong";独立的"应用程序特定路径,然后负责处理所有子路径。
因此,任何以/
开头的路径都将由该StaticFiles
应用程序处理,并且由于在参数中指定了html=True
,index.html
将自动加载而不管创建一个指向根路径/
的单独端点并尝试返回其他内容,如问题中给出的示例所示。
订单事项
例如,如果在定义@app.get("/")
端点后移动了app.mount("/",StaticFiles(...
行,则会发现顺序很重要,并且由于端点是按顺序评估的,因此不会再自动加载index.html
。请注意,在您的情况下,您可能会得到一个Internal Server Error
,因为您的@app.get("/")
端点将被调用并尝试查找custom.html
,但如果该文件不在根/
目录下,而是在/static
目录下(如代码所示),那么您将得到File does not exist
错误,因此,您应该返回FileResponse('static/custom.html')
。
即使删除了html=True
,但将StaticFiles
安装到根目录并在/
端点之前定义,在尝试访问http://localhost:8000/
时也会收到{"detail":"Not Found"}
错误响应。这是因为/
路由仍将由StaticFiles
应用程序处理(如前所述),因此您需要指定要访问的文件(当不使用html=True
时),例如http://localhost:8000/index.html
。即使您在代码中定义了其他端点(例如,/register
、/login
、/hello
),只要StaticFiles
安装到根目录(即,/
)并在代码中之前定义了所有其他端点,例如:
app.mount('/', StaticFiles(directory='static'), name='static')
@app.post('/register')
async def register():
pass
@app.post('/login')
async def login():
pass
@app.get('/hello')
async def hello():
pass
对这些路由的每个请求都将再次由StaticFiles
应用程序处理,因此会导致错误响应,例如{"detail":"Not Found"}
(如果您发送GET
请求,例如当您在浏览器的地址栏中键入URL,然后按Enter键,并且给定的路径与static
web目录中的文件名不匹配),或{detail": "Method Not Allowed"}
(如果您通过Swagger UI或其他客户端平台/应用程序发出POST
请求)。如Starlette关于StaticFiles
的文档中所述(同样参见StaticFiles
类实现):
对于不匹配的请求,静态文件将使用
404 Not found
或405 Method not allowed
响应。在HTML模式下,如果404.html
文件存在,它将显示为404响应。
因此,您应该将StaticFiles
实例装载到不同的/唯一的路径,例如/static
(即app.mount('/static', ...
,如本答案顶部所示),或者,如果您仍然希望将StaticFiles
实例装载到/
路径,请在声明所有API端点后定义StaticFiles
,例如:
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
app = FastAPI()
@app.post('/register')
async def register():
pass
@app.post('/login')
async def login():
pass
@app.get('/hello')
async def hello():
pass
@app.get('/')
async def index():
return FileResponse('static/custom.html')
app.mount('/',StaticFiles(directory='static', html=True), name='static')
备注
每次加载网页时,浏览器都会缓存页面上的大部分内容,以缩短加载时间(用户下次加载页面时)。因此,如果您尝试前面提供的示例,即在每个API端点之前定义StaticFiles
应用程序,然后使用同一浏览器会话,在所有API端点之后使用定义的StaticFiles
应用程序尝试上面的示例,但浏览器仍然显示static/index.html
文件的内容,而不是static/custom.html
——当在浏览器中访问http://localhost:8000/
时——这是由于浏览器从缓存加载网页。为了克服这个问题,你可以清除浏览器的缓存,或者在隐姓埋名窗口中打开网页(完成后关闭它),或者在浏览器中按Ctrl+F5,而不是只按F5(使用隐名或常规窗口),这将迫使浏览器从服务器检索网页,而不是从缓存加载网页。
关于FastAPI中端点的顺序,您可能也会发现这个答案很有用。
html=True
选项
将StaticFiles
实例的html
参数设置为True
(即html=True
),只需一行代码就可以简单地为web内容目录提供服务。如果您只需要提供静态文件,例如package-docs目录,那么这就是方法。但是,如果您需要提供将被动态更新的不同HTML文件,并且您希望创建其他路由/端点,您应该更好地查看Templates
(而不是FileResponse
),以及将您的StaticFiles
实例装载到不同的路径(例如/static
),而不是根路径(并且不使用html=True
)。