FastAPI / uvicorn(或 hypercorn):我的根路径在哪里?

ntg*_*ntg 6 python fastapi uvicorn hypercorn

基于一些 FastAPI 教程(包括本教程),我制作了一个简单的 FastAPI 应用程序:

from fastapi import FastAPI, Request
app = FastAPI() # also tried FastAPI(root_path="/api/v1")

@app.get("/app")
def read_main(request: Request):
    return {"message": "Hello World", "root_path": request.scope.get("root_path")}
Run Code Online (Sandbox Code Playgroud)

我想在 root 以外的路径(例如 /api/vi)...再次基于大多数教程和常识,我尝试以以下方式启动它:

uvicorn main:app --root-path /api/v1
Run Code Online (Sandbox Code Playgroud)

服务正常(在http://127.0.0.1:8000),但是,root-path似乎被忽略:任何GET请求都会http://127.0.0.1:8000/给出:

message "Hello World"
root_path   "/api/v1"
Run Code Online (Sandbox Code Playgroud)

以及任何GET要求http://127.0.0.1:8000/api/v1提供:

detail  "Not Found"
Run Code Online (Sandbox Code Playgroud)

我希望这些请求会产生相反的结果......这是怎么回事?!?

我还尝试初始化 FastAPIFastAPI(root_path="/api/v1")以及切换到 hypercorn 但无济于事...

应用程序版本的详细信息(我可能也尝试过其他一些应用程序,尽管这些应该是最新尝试的):

python                    3.9.7           hf930737_3_cpython    conda-forge
fastapi                   0.85.1             pyhd8ed1ab_0    conda-forge
uvicorn                   0.20.0           py39h06a4308_0  
hypercorn                 0.14.3           py39hf3d152e_1    conda-forge
Run Code Online (Sandbox Code Playgroud)

Chr*_*ris 6

正如 @MatsLindh 在评论部分所指出的,root_path( 或--root-path)不会更改应用程序的前缀路径,而是为代理情况后面指定,其中“您可能需要使用像 Traefik 或 Nginx 这样的代理服务器,其配置添加了您的应用程序看不到的额外路径前缀”(请参阅​​相关文档)。

文档中所述:

具有剥离路径前缀的代理

在这种情况下,拥有一个带有剥离路径前缀的代理意味着您可以 /app在代码中声明一个路径 at ,但是随后,您在顶部(代理)添加一个层,该层会将您的 FastAPI 应用程序置于类似 的路径下/api/v1

在这种情况下,原始路径/app实际上将在 处提供服务 /api/v1/app

即使您的所有代码都是假设只有/app.

并且代理会在将请求传输到 Uvicorn 之前动态“剥离”路径前缀,让您的应用程序确信它正在为 提供服务/app,这样您就不必更新所有代码以包含前缀/api/v1

到这里,一切都会正常进行。

但是,当您打开集成文档 UI(前端)时,它会期望在 处获取 OpenAPI 架构/openapi.json,而不是 /api/v1/openapi.json

因此,前端(在浏览器中运行)将尝试访问 /openapi.json但无法获取 OpenAPI 架构(它将显示“无法加载 API 定义”错误)。

因为我们的应用程序有一个路径前缀为 的代理/api/v1,所以前端需要在 处获取 OpenAPI 架构 /api/v1/openapi.json。文档 UI 还需要 OpenAPI 架构来声明此 API 服务器位于/api/v1(代理后面)。

为此,您可以使用命令行选项--root-path

 uvicorn main:app --root-path /api/v1
Run Code Online (Sandbox Code Playgroud)

[...]

或者,如果您无法提供类似--root-path或等效的命令行选项,则可以root_path 在创建 FastAPI 应用程序时设置参数:

app = FastAPI(root_path="/api/v1")
Run Code Online (Sandbox Code Playgroud)

选项1

因此,在您的情况下,由于您没有使用代理,而是需要为 API 提供自定义前缀,因此您可以使用,它允许您为 API 路由APIRouter定义 a (请注意,不得包含最终的)。您可以在实例化(例如)时给出或使用,如文档中所述,这将允许您多次包含具有不同前缀的同一路由器:prefixprefix/prefixAPIRouterrouter = APIRouter(prefix='/api/v1').include_router()

您还可以.include_router()使用不同的前缀多次使用同一路由器。

这可能很有用,例如,在不同的前缀下公开相同的 API,例如/api/v1/api/latest

这是一种高级用法,您可能并不真正需要,但如果您需要,它就在那里

可以通过http://127.0.0.1:8000/api/v1/app/app访问以下示例中的端点。

工作示例

 uvicorn main:app --root-path /api/v1
Run Code Online (Sandbox Code Playgroud)

一旦您拥有多个版本的 API 端点,您可以使用:

app = FastAPI(root_path="/api/v1")
Run Code Online (Sandbox Code Playgroud)

选项2

或者,也可以使用所需的方式安装子应用程序prefix,如本答案本答案中所示(请参阅选项 3)。

文档中所述:

当您如上所述安装子应用程序时,FastAPI 将使用 ASGI 规范中称为root_path.

这样,子应用程序就会知道使用文档 UI 的路径前缀。

并且子应用程序也可以有自己安装的子应用程序,并且一切都会正常工作,因为 FastAPI 会root_path自动处理所有这些。

因此,在下面给出的示例中,您可以/app应用程序(http://127.0.0.1:8000/app/app )访问端点,从应用程序( http://127.0.0.1:8000/api/v1)访问端点/应用程序同样,Swagger UI autodocs 可以分别通过http://127.0.0.1:8000/docshttp://127.0.0.1:8000/api/v1/docs访问。

工作示例

from fastapi import FastAPI
from fastapi.routing import APIRouter


router = APIRouter()


@router.get('/app')
def main():
    return 'Hello world!'


app = FastAPI()
app.include_router(router, prefix='/api/v1')
Run Code Online (Sandbox Code Playgroud)