如何在FastAPI中正确路由子页面?

huangapple go评论161阅读模式
英文:

How do I route subpages correctly with FastAPI?

问题

我正在尝试使用Sveltekit和FastAPI来提供一个小型Web应用程序。

FastAPI提供了一个API和各种函数,前端是使用Sveltekit构建的,使用了adapter-static

基本框架如下:

from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles

app = FastAPI()
api = FastAPI(root_path="/api")

# 挂载API
app.mount("/api", api)

# 在根路径挂载Sveltekit静态文件
app.mount('/', StaticFiles(directory="./webapp/build/", html=True), name="webapp")

# 定义API端点
@api.websocket("/something")
async def do_something(request: Request):
    body = await request.json()
    result = do_something_fancy(body)
    return {"result": result}

...

我遇到的问题是,前端应用程序定义了多个子页面:

  • 主页/仪表板位于localhost:8888
  • 设置页面位于localhost:8888/settings
  • 关于页面位于localhost:8888/about

现在,如果我在从根URL“/”开始后,使用我的Svelte应用程序导航栏导航到这些页面中的任何一个,一切都正常,但如果我直接在浏览器地址栏中输入http://localhost:8888/settings导航到它,我会收到404错误并看到{"detail":"Not Found"}

当我在一个子页面(settingsabout)上按下“刷新”或Ctrl-r时,我也会得到{"detail":"Not Found"},但在根URL上却可以正常工作。

我相当确定我只是忽略了一些简单的东西,比如将路由添加到静态应用程序挂载点,但FastAPI文档没有明确说明应该如何工作(或者似乎暗示它应该“正常工作”)。

英文:

I'm trying to serve a small web app using Sveltekit and FastAPI.

FastAPI provides an api and various functions and the frontend was built with Sveltekit using adapter-static.

The basic framework is:

from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles

app = FastAPI()
api = FastAPI(root_path="/api")

# mount the api
app.mount("/api", api)

# mount the sveltekit static files at root
app.mount('/', StaticFiles(directory="./webapp/build/", html=True), name="webapp")

# define the api endpoints
@api.websocket("/something")
async def do_something(request: Request):
    body = await request.json()
    result = do_something_fancy(body)
    return {"result": result}

...

What I'm having trouble with is that the frontend app defines multiple sub pages:

  • a main page/dashboard at localhost:8888
  • a settings page at localhost:8888/settings
  • an about page at localhost:8888/about

Now if I navigate to any of these pages using my svelte app navbar after starting at the root url "/", everything works fine, but if I navigate directly to http://localhost:8888/settings by entering it in the browser address bar, I get a 404 error and see {"detail":"Not Found"}.

I also get the {"detail":"Not Found"} when I hit "Refresh"/Ctrl-r in my browser when on one of the subpages (settings or about), but it works fine at the root url.

I'm pretty sure I'm just missing something simple like adding routing to the static app mount point, but the FastAPI docs don't specify how this should work (OR, it seems to indicate that it should "just work").

答案1

得分: 0

我找到了一种解决方法,尽管它并不完全符合我的需求,而且看起来不太优化。

通过这个回答,我对发生的情况有了更多了解:https://stackoverflow.com/a/73113792/13752965

基本上,FastAPI 只知道要提供的是根 URL / 下的 index.html 文件。似乎我可以通过使用模板响应来获得我想要的内容,但我实际上不想要模板,我只想提供由我的 Sveltekit 前端生成的 HTML 文件。

我当前的解决方法是,在请求子页面时只是重定向回主页面:

@app.get('/settings', response_class=RedirectResponse)
async def get_settings():
    return "/"

@app.get('/about', response_class=RedirectResponse)
async def get_about():
    return "/"

# 挂载 Web 应用程序
app.mount("/", StaticFiles(directory="./webapp/build/", html=True), name="webapp")

如果在挂载静态文件之前执行此操作,那么至少用户会被重定向回主页面,而不会看起来像应用程序出现故障。

英文:

I have found a workaround, although it's not exactly what I want and seems non-optimal.

I've gained a bit more understanding about what's going on thanks to this answer: https://stackoverflow.com/a/73113792/13752965

Basically, the only thing that FastAPI knows to serve is the index.html file at my root url /. It seems like I may be able to get what I want by using template responses, but I don't really want templates, I just want to serve the html files that I've generated with my Sveltekit frontend.

My current workaround, is to just redirect back to the main page when requesting one of the subpages:

@app.get('/settings', response_class=RedirectResponse)
async def get_settings():
    return "/"

@app.get('/about', response_class=RedirectResponse)
async def get_about():
    return "/"

# mount the web app
app.mount("/", StaticFiles(directory="./webapp/build/", html=True), name="webapp")

If this is done prior to mounting the static files, then at least the user is sent back to the main page, rather than it looking like the app is broken.

答案2

得分: 0

以下是翻译好的内容:

这确实是一件小事。

问题在于我的Sveltekit构建设置为直接在构建目录中输出页面:

  • settings.html
  • about.html

然而,从starlette继承的FastAPI挂载StaticFiles对象的默认行为是提供位于相应URL的index.html页面:

  • settings/index.html
  • about/index.html

我没有理解Sveltekit文档中这一部分的细微差别:
https://kit.svelte.dev/docs/page-options#trailingslash

因此,解决方法是在您的routes/+layout.js文件中添加

export const trailingSlash = 'always';

这会促使Sveltekit构建过程输出一个所需的index.html和文件夹结构,以便在FastAPI中挂载后正确运行。

这个答案提供了关于FastAPI挂载过程和许多FastAPI文档中不清楚的“坑”的详细信息。

英文:

It was indeed a minor thing.

The problem was that my Sveltekit build was set to output pages directly in the build directory:

  • settings.html
  • about.html

However, the default behaviour for mounting FastAPI (inherited from starlette) StaticFiles objects is to serve index.html pages located at the corresponding url:

  • settings/index.html
  • about/index.html

I didn't appreciate the nuance in this section of the Sveltekit docs:
https://kit.svelte.dev/docs/page-options#trailingslash

So, the solution is to add

export const trailingSlash = 'always';

to your routes/+layout.js file. This prompts the Sveltekit build process to output an index.html and folder structure that is required for proper function after mounting in FastAPI.

This answer provides good detail about the FastAPI mount process and many of these "gotchas" that aren't clear in the FastAPI documentation.

huangapple
  • 本文由 发表于 2023年7月11日 02:06:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/76656259.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定