
在 FastAPI 中,依赖项可以是任何可调用的对象,如函数、类方法等。这些依赖项可以接受参数,这些参数同样可以是其他依赖项,从而形成依赖关系链。FastAPI 会自动处理这些依赖关系,确保在调用主功能(如API路由处理函数)之前,所有依赖项都已正确解析和调用。
代码重用:通过依赖注入,可以在多个API端点中重用相同的功能或数据访问逻辑。
解耦:依赖注入有助于将应用程序的不同部分解耦,使其更容易管理和扩展。
易于测试:依赖注入使得单元测试更加简单,因为你可以轻松地为特定的依赖提供模拟(mock)或替代实现。
灵活性和可扩展性:可以根据需要轻松添加或修改依赖项,而不会影响到使用这些依赖项的主功能。
def check_item_id(item_id: int, x: int) -> int:
"""
check_item_id 函数有两个参数:item_id 和 x
其中 item_id 是一个路径参数,因为它直接与路径操作 @app.get('/items/{item_id}') 中的 {item_id} 对应
而 x 没有在路径中定义,也没有提供默认值,因此 FastAPI 会将其视为查询参数
"""
print('check item id')
if item_id > 100:
return item_id * x
else:
raise HTTPException(status_code = 404, detail = "User not found")
@app.get('/items/{item_id}')
async def read_item(item_id: int = Depends(check_item_id)):
print('read item')
return {'item_id': item_id}
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
def query_extractor(depend_q: str = Query(..., min_length = 3)):
return hashlib.sha256(depend_q.encode('utf-8')).hexdigest()
def path_extractor(depend_item_id: int = Path(..., gt = 0)):
return hashlib.sha256(depend_item_id.to_bytes(4, 'big', signed = True)).hexdigest()
def body_extractor(depend_item: Item = Body(...)):
return hashlib.sha256(depend_item.model_dump_json().encode('utf-8')).hexdigest()
@app.post("/items/{depend_item_id}")
def read_item_2(query: str = Depends(query_extractor),
depend_item_id: int = Depends(path_extractor),
item: Item = Depends(body_extractor)):
"""
:param query: 查询参数作为query_extractor的入参,请求时的参数需要为depend_q
:param depend_item_id: 路径参数作为path_extractor的入参,请求时的参数需要为depend_item_id
:param item: Body 参数作为body_extractor的入参,请求时的参数需要为depend_item
请求:
curl -X 'POST' \
'http://127.0.0.1:18081/items/12334?depend_q=hello%2Cworld' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"name": "测试",
"description": "描述信息",
"price": 12.34,
"tax": 56.78
}'
响应:
{
"item_id": "17c73e617248212e4adc5e83c87e20e51df159532284996a19c28e15eac3326f",
"query": "77df263f49123356d28a4a8715d25bf5b980beeeb503cab46ea61ac9f3320eda",
"item": "4765dec528b96cb04e46f0dd6b039b8dad29c797e4a6e4455cd244b7062e1009"
}
"""
return {"item_id": depend_item_id, "query": query, "item": item}

class User(BaseModel):
username: str
full_name: str = None
# 依赖函数使用路径参数
def extract_item_id(item_id: int = Path(...)):
return item_id
# 依赖函数使用查询参数
def pagination(skip: int = Query(0, ge = 0), limit: int = Query(10, ge = 0)):
return {"skip": skip, "limit": limit}
# 依赖函数使用Header参数
def get_user_agent(user_agent: str = Header(...)):
return user_agent
# 依赖函数使用Cookie参数
def get_session_id(session_id: str = Cookie(None)):
return session_id
# 依赖函数使用Body参数
def user_data(user: User = Body(...)):
return user
@app.get("/items/{item_id}")
def read_item(item_id: int = Depends(extract_item_id),
pagination_params: dict = Depends(pagination),
user_agent_str: str = Depends(get_user_agent),
session: str = Depends(get_session_id)):
return {
"item_id": item_id,
"pagination": pagination_params,
"user_agent": user_agent_str,
"session_id": session
}
@app.post("/users/")
def create_user(user: User = Depends(user_data)):
return {"username": user.username, "full_name": user.full_name}
在 FastAPI 中,依赖树是指依赖项之间的层次结构或关系网,其中一个依赖项可以依赖于另一个依赖项,形成一个树状结构。这种结构允许开发者构建复杂而高效的应用程序,通过将逻辑和功能分解成更小、更可管理的部分来提高代码的可维护性和重用性。
# 模拟的用户数据库
users_db = {
"alice": {"username": "alice", "role": "admin"},
"bob": {"username": "bob", "role": "user"}
}
class User:
def __init__(self, username: str, role: str):
self.username = username
self.role = role
def get_current_user(username: str):
if username in users_db:
user_data = users_db[username]
return User(username = user_data["username"], role = user_data["role"])
else:
raise HTTPException(status_code = 404, detail = "User not found")
def verify_admin(user: User = Depends(get_current_user)):
if user.role != "admin":
raise HTTPException(status_code = 403, detail = "Admin privileges required")
return user
@app.get("/admin-data/")
def admin_data(user: User = Depends(verify_admin)):
"""
GET /admin-data/:这个路径操作依赖于 verify_admin 函数
verify_admin 函数本身依赖于 get_current_user 函数来获取当前用户
verify_admin 函数是 admin_data 路径操作的直接依赖,而 get_current_user 是 verify_admin 的子依赖
这种方式允许你构建复杂的依赖关系,每个依赖项负责处理特定的任务,从而保持代码的清晰和模块化
"""
return {"message": "Admin data accessed", "user": user.username}
def get_user_id(test_id: int = Path(..., alias = "aliased_user_id")):
# 这里的 id 对应于路径中的 user_id
return hashlib.sha3_512(test_id.to_bytes(8, 'big', signed = True)).hexdigest()
@app.get("/users/{aliased_user_id}")
def read_user(user_id: int = Depends(get_user_id)):
"""
Path 和 Query 的参数名不必总是与 Depends 函数中的参数名保持一致
可以通过在 Path 和 Query 的定义中使用 alias 参数来指定如何从请求中提取这些值,并将它们映射到依赖函数的参数上
在这段代码中,路径参数 aliased_user_id 与 get_user_id 函数中的 test_id 参数
通过 alias = "aliased_user_id" 保持一致的
"""
return {"user_id": user_id}在上面的代码示例中,我们可以看到一个依赖树的实际应用:
以 /admin-data/ 路径操作为例,我们可以看到以下依赖关系:
这里,get_current_user 是底层依赖,它直接与数据交互(从模拟的用户数据库中获取用户信息)。verify_admin 是中间层依赖,它使用 get_current_user 的输出来验证用户是否具有管理员权限。最后,admin_data 是顶层依赖,它依赖于 verify_admin 的验证结果来决定是否允许用户访问特定的数据。
依赖树的优势
class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]):
"""
Class-Based Dependency Injection
FastAPI will:
Create an instance of CommonQueryParams
Extract query parameters from the request
Pass them to the class constructor
Inject the resulting instance into your endpoint
Key benefits of class-based dependencies:
Encapsulation: Group related parameters and methods
Reusability: Use the same dependency in multiple endpoints
Maintainability: Organize code in a more structured way
Testability: Easier to mock and test
Hierarchical Dependencies: Classes can depend on other classes
"""
response = {}
class_json_str = json.dumps(commons, ensure_ascii = False, default = lambda o: o.__dict__)
h_str = hashlib.sha256(class_json_str.encode('utf-8')).hexdigest()
response.update({"sha256_str": h_str})
return response
async def verify_token(x_token: Annotated[str, Header()]):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code = 400, detail = "X-Token header invalid")
async def verify_key(x_key: Annotated[str, Header()]):
if x_key != "fake-super-secret-key":
raise HTTPException(status_code = 400, detail = "X-Key header invalid")
return x_key
@app.get("/items/", dependencies = [Depends(verify_token), Depends(verify_key)])
async def read_items():
"""
Header-Based Authentication using multiple dependencies
"""
return [{"item": "Foo"}, {"item": "Bar"}]
from __future__ import annotations
import uvicorn
from fastapi import Depends, FastAPI, Header, HTTPException
from typing_extensions import Annotated
async def verify_token(x_token: Annotated[str, Header()]):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code = 400, detail = "X-Token header invalid")
async def verify_key(x_key: Annotated[str, Header()]):
if x_key != "fake-super-secret-key":
raise HTTPException(status_code = 400, detail = "X-Key header invalid")
return x_key
"""
Global Dependencies
"""
app = FastAPI(dependencies = [Depends(verify_token), Depends(verify_key)])
@app.get("/items/")
async def read_items():
return [{"item": "Portal Gun"}, {"item": "Plumbus"}]
@app.get("/users/")
async def read_users():
return [{"username": "Rick"}, {"username": "Morty"}]
if __name__ == '__main__':
uvicorn.run(app, host = '127.0.0.1', port = 18081)

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。