fastapi是一个非常强大而又直观的依赖注入系统
它被设计得使用起来非常简单,同时让开发人员可以轻松得将其他组件与fastapi集成在一起。
什么时依赖注入
依赖注入意味着在编码得时候,在你得代码中会留一条通道给你去声明需要用到依赖,(即:要想你得代码成功运行必须得先成功运行依赖函数。有点类似于装饰器)
在你做以下操作得时候将会非常有用:
- 使用共享逻辑(一遍又一遍得使用相同得代码)
- 共享数据连接
- 强制安全,授权,角色需要等
第一步:
让我们先看一个例子,这个例子在实际应用中根本没什么用,但是可以关注一下依赖注入是如何运行得。
首先建立被依赖得函数,它仅仅是一个函数它可以拥有所有得路径函数得参数
from fastapi import Depends, FastAPI
app = FastAPI()
async def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
return commons
@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
return commons
它和路径操作函数拥有相同得结构,你甚至可以理解为,它就是没有装饰器得路径操作函数。
并且它可以返回你想要得任何东西,
在这个例子中被依赖函数将希望接收:
1、一个可选择得查询参数q,str类型
2、一个带有默认值为0得skip查询参数,int类型
3、一个带有默认值为100得limit查询参数,int类型
然后将他们转换为字典再返回
声明依赖,
依赖得声明就和Body,Query等一样,需要使用Depends。
但是他们也有一些不同得地方:
depends中只有一个被依赖得参数,
无论什么时候请求到达后,fastapi将做一下操作:
1、调用依赖函数,并使用正确得参数。
2、从函数中得到返回。
3、分配返回结果给你得路径操作函数得参数。
简单得使用
和所有得web框架一样,路径操作函数被声明来匹配路径,然后fastapi将调用函数并传入正确得参数,并处理响应。
你并不会直接得调用这些函数,他们被框架调用。
在依赖注入系统中,你可以告诉fastapi你得路径操作函数依赖于其他得东西。并且fastapi将自动处理并注入结果。
其它使用依赖注入思想的条目:
- resource
- providers
- services
- injectables
- components
简单而强大
尽管依赖注入系统定义和使用上都非常简单,但是它却非常的强大。
你可以嵌套依赖,
最后,构建了层次结构的依赖关系树,并且依赖关系注入系统将为您解决所有这些依赖关系(及其子依赖关系)并在每个步骤中提供(注入)结果。
例如,假设您有4个API端点(path操作):
/items/public/
/items/private/
/users/{user_id}/activate
/items/pro/
那么您可以为每个依赖项和子依赖项添加不同的权限要求:

类作为依赖项
直到现在,所使用的都是依赖被声明为函数。
但是那并不是唯一的依赖声明。
实际上依赖只要能被“callable”就行。
在python中这叫鸭子类型,只要实现了__call__()函数的所有类型都可以是callable。
from fastapi import Depends, FastAPI
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
class CommonQueryParams:
def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response
这些类中的初始化参数都是从requests中获得。
type annotation vs depends
以上的例子中commons定义:
commons: CommonQueryParams=Depends(CommonQueryParams)
实际可以使用:
commons=Depends(CommonQueryParams)
以及:
commons: CommonQueryParams = Depends()
都是一样的效果。
子依赖
你可以创建依赖嵌套,而且想嵌套多少层都可以。
eg:
from fastapi import Cookie, Depends, FastAPI
app = FastAPI()
def query_extractor(q: str = None):
return q
def query_or_cookie_extractor(
q: str = Depends(query_extractor), last_query: str = Cookie(None)
):
if not q:
return last_query
return q
@app.get("/items/")
async def read_query(query_or_default: str = Depends(query_or_cookie_extractor)):
return {"q_or_cookie": query_or_default}
多次使用相同依赖
例如,如果为同一个路径操作多次声明一个依赖项,例如,多个依赖项具有一个公共的子依赖项,则FastAPI将知道每个请求仅调用一次该子依赖项。
并且它将返回的值保存在“缓存”中,并将其传递给该特定请求中需要它的所有“依赖项”,而不是针对同一请求多次调用依赖项。
在高级场景中,您需要在同一请求中的每个步骤(可能多次)上调用依赖项,而不是使用“ cached”值,可以use_cache=False在使用时设置参数Depends:
async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)):
return {“fresh_value”: fresh_value}
依赖在路径操作装饰器中
有时候你的依赖中其实并不需要返回值。在这种情况下你可以将依赖添加到路径操作装饰器中的list。
from fastapi import Depends, FastAPI, Header, HTTPException
app = FastAPI()
async def verify_token(x_token: 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: 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():
return [{"item": "Foo"}, {"item": "Bar"}]
这些依赖将会被执行,但是他们的返回值将不会传入路径操作函数中。
Dependencies with yield
fastapi支持依赖在结束的时候执行一些额外的步骤。
为了实现此功能,需要将return换成yield,然后写入额外的步骤。
例如:
你可以创建数据库连接然后在完成的时候关闭它。
async def get_db():
db = DBSession()
try:
yield db
finally:
db.close()
Dependencies with yield and HTTPException
上一个例子中你可以看到,可以使用yield语句并且使用try去捕获异常。
你也许想要去抛出HTTPException或退出码,在yeild中,但是这将不会起作用。

只有一种response将会被发送到客户端,那就是error response 或者路径操作的response。一旦这些response被发送后,其他的response将不会被发送到客户端。
上下文管理器
什么是上下文管理器,其实就是多有的可以使用with语句的python对象:
with open("./somefile.txt") as f:
contents = f.read()
print(contents)
当with 块结束后,可以保证关闭文件,即使有异常。
当你使用yield语句创建依赖,fastapi将会在内部将它转换为上下文管理器,并将它与其他一些相关联的工具相结合。
Using context managers in dependencies with yield
在python中你可以创建上下文管理器,通过在对象内部定义__enter__()和__exit__()方法。
class MySuperContextManager:
def __init__(self):
self.db = DBSession()
def __enter__(self):
return self.db
def __exit__(self, exc_type, exc_value, traceback):
self.db.close()
async def get_db():
with MySuperContextManager() as db:
yield db
你也可以在fastapi中的with或者async with语句中使用yield来定义依赖函数。
其他的定义上下文管理器的方式:
@contextlib.contextmanager
@contextlib.asynccontextmanager
使用这些装饰器并使用yield做返回。但是在fastapi中你不需要使用装饰器,直接使用yield语句。
高级依赖
参数依赖
我们看到的所有依赖项都是固定的函数或类。
但是在某些情况下,您可能希望能够在依赖项上设置参数,而不必声明许多不同的函数或类。
假设我们想要一个依赖项来检查查询参数是否q包含一些固定内容。
但是我们希望能够参数化该固定内容。
from fastapi import Depends, FastAPI
app = FastAPI()
class FixedContentQueryChecker:
def __init__(self, fixed_content: str):
self.fixed_content = fixed_content
def __call__(self, q: str = ""):
if q:
return self.fixed_content in q
return False
checker = FixedContentQueryChecker("bar")
@app.get("/query-checker/")
async def read_query_check(fixed_content_included: bool = Depends(checker)):
return {"fixed_content_in_query": fixed_content_included}
__call__就是FastAPI将用于检查其他参数和子依赖项的方法,并且稍后将调用该方法将值传递给路径操作函数中的参数。
本文深入探讨了FastAPI框架中的依赖注入系统,解释了依赖注入的概念,如何在代码中声明和使用依赖,以及依赖注入的强大功能,包括嵌套依赖、多次使用相同依赖和在路径操作装饰器中使用依赖。
846

被折叠的 条评论
为什么被折叠?



