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)
正如 @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:Run Code Online (Sandbox Code Playgroud)uvicorn main:app --root-path /api/v1[...]
或者,如果您无法提供类似
--root-path或等效的命令行选项,则可以root_path在创建 FastAPI 应用程序时设置参数:Run Code Online (Sandbox Code Playgroud)app = FastAPI(root_path="/api/v1")
因此,在您的情况下,由于您没有使用代理,而是需要为 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)
或者,也可以使用所需的方式安装子应用程序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/docs和http://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)