如何在 FastAPI 后端上提供 React 构建的前端?

Jof*_*yab 8 reactjs react-fullstack fastapi

我尝试将前端安装到/with app.mount,但这会使我的所有/api路由无效。我还尝试了以下代码将文件夹安装到/static各自的路由中并index.html在以下位置提供文件/

@app.get("/")
def index():
    project_path = Path(__file__).parent.resolve()
    frontend_root = project_path / "client/build"
    return FileResponse(str(frontend_root) + '/index.html', media_type='text/html')

static_root = project_path / "client/build/static"
app.mount("/static", StaticFiles(directory=static_root), name="static")
Run Code Online (Sandbox Code Playgroud)

这大部分有效,但client/build文件夹中包含的文件未安装,因此无法访问。我知道 Node.js 有一种通过相对路径为前端页面提供服务的方法res.sendFile("index.html", { root: </path/to/static/folder });。FastAPI 中是否有等效的函数可以执行此操作?

Ric*_*rdo 18

clmno的解决方案是两台服务器+路由。Jay Jay Cayabyab正在 API 上寻找一个端点,为 Webpacked SPA 提供服务,就是您想要的那种npm run build。我一直在寻找完全相同的解决方案,因为这就是我正在使用 Flask 所做的事情,并且我正在尝试用 FastAPI 替换 Flask。

FastAPI 的文档中多次提到它是基于 starlette 的。在 starlette 上搜索提供 SPA 服务时,我发现了这个问题的回复。当然,这对我来说并不是现成的,因为我错过了一些在建议的解决方案中未提及的导入内容。

这是我的代码,它正在工作:

from fastapi.staticfiles import StaticFiles

class SPAStaticFiles(StaticFiles):
async def get_response(self, path: str, scope):
    response = await super().get_response(path, scope)
    if response.status_code == 404:
        response = await super().get_response('.', scope)
    return response

app.mount('/my-spa/', SPAStaticFiles(directory='folder', html=True), name='whatever')
Run Code Online (Sandbox Code Playgroud)

注意:我故意更改了端点(my-spa)、目录(文件夹)和应用程序名称(无论什么)的名称,以强调这些不必完全相同。

在本例中,您将构建的 SPA 放入该folder文件夹中。为此,请在 SPA 项目文件夹中运行npm run buildyarn run build,然后您会得到一个名为 的文件夹dist。将所有文件和文件夹复制dist到此folder文件夹中。

完成此操作后,运行 FastAPI 应用程序,然后转到http://localhost:5000/my-spa/. 为了绝对清楚起见,我使用这个特定 URL 的原因是我的应用程序有一个像这样的 main:

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=5000)
Run Code Online (Sandbox Code Playgroud)

所以它从端口 5000 开始。您的情况可能有所不同。

我讨厌这些回复中缺少导入,因为有时回复似乎从未运行过。当我输入此内容时,我的程序正在另一个屏幕上运行,因此这不会浪费您的时间。然而,假设你已经在做这些微不足道的事情,我自己可能会错过一些重要的事情

from fastapi import FastAPI
Run Code Online (Sandbox Code Playgroud)

等等。如果您尝试此操作并发现缺少任何内容,请在这里告诉我。

  • 我发现你的回答非常有帮助,我也会说我最终只是做了这里建议的事情。/sf/answers/4794177671/。谢谢您的帮助! (2认同)

Mik*_*liy 9

更新里卡多的答案,

在某个时候starlette.staticfiles.StaticFiles开始引发HTTPException而不是 PlaintText 404 响应,因此对于托管 SPA,我想新版本的代码应该如下所示:

from fastapi import HTTPException
from starlette.exceptions import HTTPException as StarletteHTTPException

# ... 

class SPAStaticFiles(StaticFiles):
    async def get_response(self, path: str, scope):
        try:
            return await super().get_response(path, scope)
        except (HTTPException, StarletteHTTPException) as ex:
            if ex.status_code == 404:
                return await super().get_response("index.html", scope)
            else:
                raise ex


app.mount("/", SPAStaticFiles(directory="dist", html=True), name="spa-static-files")
Run Code Online (Sandbox Code Playgroud)