FAST API 多次包含同一路由器,不同 prefix
如果要构建应用程序或Web API,则很少将所有内容都放在一个文件中。
FastAPI提供了一种方便的工具,可在保持所有灵活性的同时构建应用程序。
信息
如果您来自Flask,那将相当于Flask的蓝图。
示例文件结构
假设您的文件结构如下:
.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── routers
│ ├── __init__.py
│ ├── items.py
│ └── users.py
小费
有两个__init__.py
文件:每个目录或子目录一个。
这就是允许将代码从一个文件导入另一个文件的原因。
例如,app/main.py
您可以输入以下内容:
from app.routers import items
- 该
app
目录包含所有内容。 - 该
app
目录有一个空文件app/__init__.py
。- 因此,该
app
目录是“ Python包”(“ Python模块”的集合)。
- 因此,该
- 该
app
目录还有一个app/main.py
文件。- 由于它位于Python软件包目录中(因为存在一个文件
__init__.py
),因此它是该软件包的“模块”:app.main
。
- 由于它位于Python软件包目录中(因为存在一个文件
- 有一个子目录
app/routers/
。 - 子目录
app/routers
也有一个空文件__init__.py
。- 因此,它是一个“ Python子包”。
- 该文件
app/routers/items.py
在旁边app/routers/__init__.py
。- 因此,这是一个子模块:
app.routers.items
。
- 因此,这是一个子模块:
- 该文件
app/routers/users.py
在旁边app/routers/__init__.py
。- 因此,这是一个子模块:
app.routers.users
。
- 因此,这是一个子模块:
APIRouter
假设专门用于处理用户的文件是的子模块/app/routers/users.py
。
您希望将与用户相关的路径操作与其余代码分开,以使其井井有条。
但是它仍然是同一FastAPI应用程序/ Web API的一部分(它是同一“ Python包”的一部分)。
您可以使用来为该模块创建路径操作APIRouter
。
进口 APIRouter
您可以导入它,并使用与该类相同的方式创建一个“实例” FastAPI
:
from fastapi import APIRouter
router = APIRouter()
@router.get("/users/", tags=["users"])
async def read_users():
return [{"username": "Foo"}, {"username": "Bar"}]
@router.get("/users/me", tags=["users"])
async def read_user_me():
return {"username": "fakecurrentuser"}
@router.get("/users/{username}", tags=["users"])
async def read_user(username: str):
return {"username": username}
路径操作与APIRouter
然后使用它声明路径操作。
以与使用FastAPI
类相同的方式使用它:
from fastapi import APIRouter
router = APIRouter()
@router.get("/users/", tags=["users"])
async def read_users():
return [{"username": "Foo"}, {"username": "Bar"}]
@router.get("/users/me", tags=["users"])
async def read_user_me():
return {"username": "fakecurrentuser"}
@router.get("/users/{username}", tags=["users"])
async def read_user(username: str):
return {"username": username}
您可以将其APIRouter
视为“迷你FastAPI
”类。
支持所有相同的选项。
所有相同的参数,响应,依赖性,标签等。
小费
在此示例中,变量称为router
,但是您可以根据需要命名。
我们将把它包含APIrouter
在主FastAPI
应用程序中,但首先,让我们添加另一个APIRouter
。
另一个模块 APIRouter
假设您在的模块中还具有专用于处理应用程序中“项目”的端点app/routers/items.py
。
您具有以下路径操作:
/items/
/items/{item_id}
与的结构相同app/routers/users.py
。
但是,可以说这一次我们比较懒。
而且我们不需要在每个路径操作中都显式地键入/items/
和键入(稍后将能够进行操作):tags=["items"]
from fastapi import APIRouter, HTTPException
router = APIRouter()
@router.get("/")
async def read_items():
return [{"name": "Item Foo"}, {"name": "item Bar"}]
@router.get("/{item_id}")
async def read_item(item_id: str):
return {"name": "Fake Specific Item", "item_id": item_id}
@router.put(
"/{item_id}",
tags=["custom"],
responses={403: {"description": "Operation forbidden"}},
)
async def update_item(item_id: str):
if item_id != "foo":
raise HTTPException(status_code=403, detail="You can only update the item: foo")
return {"item_id": item_id, "name": "The Fighters"}
添加一些自定义的tags
,responses
和dependencies
我们既不添加前缀/items/
也不在tags=["items"]
以后添加它们。
但是,我们可以添加自定义tags
和responses
将被应用到特定的路径操作:
from fastapi import APIRouter, HTTPException
router = APIRouter()
@router.get("/")
async def read_items():
return [{"name": "Item Foo"}, {"name": "item Bar"}]
@router.get("/{item_id}")
async def read_item(item_id: str):
return {"name": "Fake Specific Item", "item_id": item_id}
@router.put(
"/{item_id}",
tags=["custom"],
responses={403: {"description": "Operation forbidden"}},
)
async def update_item(item_id: str):
if item_id != "foo":
raise HTTPException(status_code=403, detail="You can only update the item: foo")
return {"item_id": item_id, "name": "The Fighters"}
主要的 FastAPI
现在,让我们在中查看模块app/main.py
。
在这里导入和使用类FastAPI
。
这将是应用程序中将所有内容捆绑在一起的主文件。
进口 FastAPI
您可以像平常一样导入和创建一个FastAPI
类:
from fastapi import Depends, FastAPI, Header, HTTPException
from .routers import items, users
app = FastAPI()
async def get_token_header(x_token: str = Header(...)):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
app.include_router(users.router)
app.include_router(
items.router,
prefix="/items",
tags=["items"],
dependencies=[Depends(get_token_header)],
responses={404: {"description": "Not found"}},
)
导入 APIRouter
但是这次我们没有直接在中添加路径操作FastAPI
app
。
我们导入其他具有APIRouter
s的子模块:
from fastapi import Depends, FastAPI, Header, HTTPException
from .routers import items, users
app = FastAPI()
async def get_token_header(x_token: str = Header(...)):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
app.include_router(users.router)
app.include_router(
items.router,
prefix="/items",
tags=["items"],
dependencies=[Depends(get_token_header)],
responses={404: {"description": "Not found"}},
)
由于该文件app/routers/items.py
是同一Python包的一部分,因此我们可以使用“点符号”将其导入。
汇入的运作方式
这部分:
from .routers import items, users
手段:
- 从此模块(文件
app/main.py
)所在的相同包(目录app/
)开始... - 寻找子包
routers
(目录位于app/routers/
)... - 然后从中导入子模块
items
(位于的文件app/routers/items.py
)和users
(位于的文件app/routers/users.py
)...
该模块items
将具有一个变量router
(items.router
)。这与我们在文件中创建的相同app/routers/items.py
。这是一个APIRouter
。模块也一样users
。
我们也可以像这样导入它们:
from app.routers import items, users
避免名称冲突
我们将items
直接导入子模块,而不是仅导入其变量router
。
这是因为我们router
在子模块中还有另一个变量users
。
如果我们一个接一个地导入,例如:
from .routers.items import router
from .routers.users import router
在router
从users
将覆盖从一个items
,我们将无法在同一时间使用它们。
因此,为了能够在同一个文件中使用它们,我们直接导入子模块:
from fastapi import Depends, FastAPI, Header, HTTPException
from .routers import items, users
app = FastAPI()
async def get_token_header(x_token: str = Header(...)):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
app.include_router(users.router)
app.include_router(
items.router,
prefix="/items",
tags=["items"],
dependencies=[Depends(get_token_header)],
responses={404: {"description": "Not found"}},
)
包括一个 APIRouter
现在,包括router
子模块中的users
:
from fastapi import Depends, FastAPI, Header, HTTPException
from .routers import items, users
app = FastAPI()
async def get_token_header(x_token: str = Header(...)):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
app.include_router(users.router)
app.include_router(
items.router,
prefix="/items",
tags=["items"],
dependencies=[Depends(get_token_header)],
responses={404: {"description": "Not found"}},
)
信息
users.router
包含APIRouter
文件的内部app/routers/users.py
。
随着app.include_router()
我们可以添加APIRouter
到主FastAPI
应用程序。
它将包括来自该路由器的所有路由作为其一部分。
技术细节
实际上,它将为内部声明的每个路径操作在内部创建一个路径操作。APIRouter
因此,在幕后,它实际上将像一切都是同一单个应用程序一样工作。
校验
包括路由器时,您不必担心性能。
这将需要几微秒,并且只会在启动时发生。
因此,它不会影响性能。
包括APIRouter
有prefix
,tags
,responses
,和dependencies
现在,让我们包括items
子模块中的路由器。
但是,请记住,我们很懒,没有对所有路径操作都添加/items/
也不添加吗?tags
我们可以添加一个前缀的所有路径操作使用参数prefix
的app.include_router()
。
由于每个路径操作的路径都必须以开头/
,例如:
@router.get("/{item_id}")
async def read_item(item_id: str):
...
...前缀不得包含final /
。
因此,在这种情况下,前缀为/items
。
我们还可以添加一个列表,tags
该列表将应用于此路由器中包括的所有路径操作。
并且我们可以添加预定义responses
,该预定义也将包含在所有路径操作中。
我们可以添加一个列表,dependencies
该列表将被添加到路由器中的所有路径操作中,并将针对对它们的每个请求执行/解决。请注意,就像路径操作修饰符中的依赖项一样,任何值都不会传递给路径操作function。
from fastapi import Depends, FastAPI, Header, HTTPException
from .routers import items, users
app = FastAPI()
async def get_token_header(x_token: str = Header(...)):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
app.include_router(users.router)
app.include_router(
items.router,
prefix="/items",
tags=["items"],
dependencies=[Depends(get_token_header)],
responses={404: {"description": "Not found"}},
)
最终结果是项目路径现在为:
/items/
/items/{item_id}
...按照我们的意图。
- 它们将被标记为包含单个字符串的标签列表
"items"
。 - 声明了标签的路径操作
"custom"
将同时包含标签items
和custom
。- 这些“标签”对于自动交互式文档系统(使用OpenAPI)特别有用。
- 所有这些都将包括预定义的
responses
。 - 声明自定义响应的路径操作
403
将同时具有预定义的响应(404
)和403
声明中的声明。 - 所有这些路径操作将在其
dependencies
之前具有评估/执行的列表。- 如果您还在特定的路径操作中声明依赖项,它们也将被执行。
- 首先执行路由器相关性,然后
dependencies
在装饰器中执行,然后执行常规参数相关性。 - 您还可以使用添加
Security
依赖项scopes
。
小费
有dependencies
一个装饰都可以使用,例如,要求身份验证整组的路径操作。即使没有将依赖项单独添加到每个依赖项中。
校验
的prefix
,tags
,responses
和dependencies
参数(如在许多其他情况下),从仅仅是一种功能FastAPI来帮助你避免代码重复。
小费
您也可以直接添加路径操作,例如使用:@app.get(...)
。
除了app.include_router()
,在同一个FastAPI应用中。
它仍然可以正常工作。
非常技术细节
注意:这是一个非常技术性的细节,您可能可以跳过。
所述APIRouter
s的不是“安装”,它们不从应用程序的其余部分隔离。
这是因为我们希望将它们的路径操作包括在OpenAPI架构和用户界面中。
由于我们不能仅仅隔离它们并独立于其余部分来“装载”它们,因此路径操作被“克隆”(重新创建),而不是直接包含在内。
检查自动API文档
现在,运行uvicorn
,使用模块app.main
和变量app
:
信息:在http://127.0.0.1:8000上运行的Uvicorn(按CTRL + C退出)
restart↻
然后在http://127.0.0.1:8000/docs中打开文档。
您将看到使用正确的路径(和前缀)和正确的标记的自动API文档,包括来自所有子模块的路径:
多次包含同一路由器,不同 prefix
您也可以.include_router()
在同一路由器上使用不同的前缀多次使用。
例如,这可以用于以不同的前缀(例如/api/v1
和)公开相同的API /api/latest
。
这是您可能真正不需要的高级用法,但是如果您有需要,可以使用。