如果要构建应用程序或Web API,则很少将所有内容都放在一个文件中。 FastAPI提供了一种方便的工具,可在保持所有灵活性的同时构建应用程序。
:::info
信息
如果您来自Flask,那将相当于Flask的蓝图。
:::
一个示例文件结构
我们来介绍一下下面的文件结构:
.├── app│ ├── __init__.py│ ├── main.py│ └── routers│ ├── __init__.py│ ├── items.py│ └── users.py
:::info
提示
有两个__init__.py文件:每个目录或子目录中的一个。
这就是允许将代码从一个文件导入另一个文件的原因。
例如,在app/main.py中,您可以有如下一行:from app.routers import items
:::
app文件夹包含项目的所有文件app文件夹下有一个空的文件app/__init__.py.- 因此
app文件夹是一个python的包
- 因此
app文件夹也有一个app/main.py文件- 由于它位于Python软件包目录中(因为存在文件init.py),因此它是该软件包的“模块”:app.main。
- 有一个子文件夹
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.- 因此它是一个子模块
APIRouter
假设专门用于处理用户的文件是/app/routers/users.py中的子模块。
您希望将与用户相关的路径操作与其余代码分开,以使其井井有条。
但是它仍然是同一FastAPI应用程序/Web API的一部分(它是同一“Python包”的一部分)。您可以使用APIRouter为该模块创建路径操作。导入
导入它并以与FastAPI类相同的方式创建“实例”:APIRouterfrom fastapi import APIRouterrouter = APIRouter()
APIRouter路径操作
然后使用它来声明您的路径操作。
以与使用FastAPI类相同的方式使用它:
您可以将@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”类。
支持所有相同的选项。
所有相同的参数,响应,依赖性,标签等。
- 因此它是一个子模块
:::info
提示
在此示例中,该变量称为router,但您可以根据需要命名。
:::
我们将在主FastAPI应用程序中包含此APIrouter,但首先,让我们添加另一个APIRouter。
APIRouter的其他包
假设您在app/routers/items.py模块中还具有专用于处理应用程序中“Items”的端点。
您具有以下路径操作:
/items/
/items/{item_id}
所有的结构和app/routers/users.py相同
但是,可以说这一次我们比较懒。
而且,我们不想在每个路径操作中都必须显式键入/items/和tags=["items"](稍后我们将能够执行此操作):
from fastapi import APIRouter, HTTPExceptionrouter = 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"}
添加自定义标签,响应和依赖
我们没有在上面添加/items/和tags=["items"]。
但是我们可以添加自定义tags和response应用于特殊的路径操作。
from fastapi import APIRouter, HTTPExceptionrouter = 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
from fastapi import Depends, FastAPI, Header, HTTPExceptionapp = FastAPI()
导入APIRouter
但是这一次我们不是直接在FastAPI应用程序中添加路径操作。
我们导入其他具有APIRouters的子模块:
from .routers import items, users
由于文件app/routers/items.py是同一Python包的一部分,因此我们可以使用“点符号”将其导入。
导入是如何进行的。
from .router import items, users
意思是:
从此模块(文件app/main.py)所在的同一包(目录app/)开始…
寻找子包路由器(目录位于app/routers/)…
* 然后从中导入子模块项(app/routers/items.py中的文件)和用户(app/routers/users.py中的文件)…
模块items将具有可变路由器(items.router)。这与我们在文件app/routers/items.py中创建的相同。这是一个APIRouter。对于模块用户也是如此。
我们也可以用下面这种方法导入:
from app.routers import items, users
:::tips
信息 第一个版本是“相对导入”。
第二个版本是“绝对导入”。
要了解有关Python包和模块的更多信息,请阅读有关模块的Python官方文档。
:::
避免名字冲突
我们直接导入子模块项,而不是仅导入其可变路由器。
这是因为子模块用户中还有另一个名为router的变量。
如果我们一个接一个地导入,例如:
from .routers.items import routerfrom .routers.users import router
users的router将覆盖items中的一个,而我们将无法同时使用它们。
因此,为了能够在同一个文件中使用它们,我们直接导入子模块:
from fastapi import Depends, FastAPI, Header, HTTPExceptionfrom .routers import items, usersapp = 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
现在,让我们包括来自子模块users的router:
from fastapi import Depends, FastAPI, Header, HTTPExceptionfrom .routers import items, usersapp = 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"}},)
:::info
信息users.router在文件app/routers/users.py中包含APIRouter。
:::
使用app.include_router(),我们可以将APIRouter添加到主FastAPI应用程序中。
它将包括来自该路由器的所有路由作为其一部分。
:::tips
详细介绍
实际上,它将为APIRouter中声明的每个路径操作在内部创建一个路径操作。
因此,在幕后,它实际上将像一切都是同一个应用程序一样工作。
检查
使用路由器时,您不必担心性能。
这将需要几微秒,并且只会在启动时发生。
因此,它不会影响性能。
:::
包括一个带有prefix,tags,response和dependencies的APIRouter
现在,让我们包括items子模块中的路由器。
但是,请记住我们很懒,没有在所有路径操作中添加/items/也没有tags吗?
我们可以使用app.include_router()的参数prefix为所有路径操作添加前缀。
由于每个路径操作的路径都必须以/开头,例如:
@router.get("/{item_id}")async def read_item(item_id: str):...
…前缀不得包含结尾的/。
因此,在这种情况下,前缀为/items。
我们还可以添加标签列表,这些标签将应用于此路由器中包括的所有路径操作。
我们还可以添加预定义的响应,这些响应也将包含在所有路径操作中。
我们可以添加一个dependencies列表,该依赖关系将添加到路由器中的所有路径操作中,并将针对对它们的每个请求执行/解决。请注意,就像路径操作修饰符中的依赖项一样,任何值都不会传递给路径操作函数。
from fastapi import Depends, FastAPI, Header, HTTPExceptionfrom .routers import items, usersapp = 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”的标签列表。
声明“自定义”标签的路径操作将同时具有标签,项目和自定义。
这些“标签”对于自动交互式文档系统(使用OpenAPI)特别有用
所有这些都将包括预定义的ressponse。
声明自定义403响应的路径操作将同时具有预定义的响应(404)和直接声明的403。
所有这些路径操作都将在其之前评估/执行依赖项列表。
如果您还在特定的路径操作中声明依赖项,它们也将被执行。
首先执行路由器相关性,然后执行装饰器中的相关性,然后执行常规参数相关性。
* 您还可以在作用域内添加安全性依赖项
:::info
提示
例如,可以使用在装饰器中具有依赖项来要求对整个路径操作组进行身份验证。即使没有将依赖项单独添加到每个依赖项中。
:::
:::tips
检查prefix,tags,response和dependencies参数(在许多其他情况下)只是FastAPI的一项功能,可帮助您避免代码重复。
:::
:::info
提示
您也可以直接添加路径操作,例如:@app.get(...)。
在同一FastAPI应用程序中,除了app.include_router()外。
它仍然可以正常工作
:::
