在看到这个词汇的时候,没有任何的概念。在翻阅一些资料之后,有了一点点眉目。对于类A,要是实现A的功能,必须要类B的功能。所以在A中实例化一个B。一旦B需要重构,由于A几乎完全依赖与B,所以A几乎也要重构。这是一种相当耦合的模式,依赖注入就是为了解决这种耦合性。A不再new一个B的实例,而是让B的一个实例作为A的一个成员存在,A不再关注B的实例化,只关注B的方法。(这是我的理解,也许有不对的地方)
在FastApi的框架中,依赖注入用于解决重复的逻辑代码,分享数据库的链接,统一验权等功能。旨在减少代码重复。
async def pagination(page_num: int = 1, page_count: int = 10):
return {"page_num": page_num, "page_count": page_count}
@app.get("/request01")
async def request01(*, page: dict = Depends(pagination), c: int):
return [page, c]
使用Depends实现依赖注入,pagination方法接收的page_num当前页码数,page_count每页的数据量,经过处理后返回给page一个起始结束下标的范围字典。在这个例子中来看,和其实现的功能和装饰器有点像,对于request01,不关注我接受了什么数据,只希望获取分页的下标范围,而pagination方法实现了该功能,这样当分页的数据格式发生变更时,也只需要改变pagination方法。
上面注入的依赖是以函数的形式,其实对于FastApi,能够作为依赖的关键是是否能够被调用(callable),所以Python class类也可以作为依赖被注入。
class Pagination:
def __init__(self, page_num: int = 1, page_count: int = 10):
self.page_start = (page_num - 1) * page_count
self.page_end = page_num * page_count - 1
@app.get("/request02")
async def request02(*, page: Pagination = Depends(Pagination)):
return page
Pagination作为类依赖被注入到request02中,注意Pagination的__init__方法创建了一个Pagination的实例。
async def pagination1(page: dict = Depends(pagination), other: int = 0):
page.update({"other": other})
return page
@app.get("/request03")
async def request03(page: dict = Depends(pagination1)):
return page
request03方法中依赖了pagination1, 而pagination1中又依赖了pagination,这是依赖的嵌套。在最终的调用中,使用最底层的参数,例如在调用request03时,传入的参数是other, 以及pagination中的page_num, page_count。
async def verify_header(token: str = Header(...)):
print(token)
async def verify_query(name: str = Query(...)):
print(name)
@app.get("/request04", dependencies=[Depends(verify_header), Depends(verify_query)])
def request04():
return "hello world"
当我不关注依赖的返回值时,可以将依赖注入到路由装饰器中,使用dependencies关键字,数组类型的依赖。无论依赖函数是否有返回,在接口函数中都不会使用。
# 中间件简介
@app.middleware("http")
async def get_response_time(request: Request, call_next):
start_time = datetime.now()
response = await call_next(request)
end_time = datetime.now()
print(end_time.timestamp() - start_time.timestamp())
return response
当前只支持http的中间件,在await call_next()之前初始化一些数据,在之后是调用完接口,返回响应之前。在这里可以记录接口的响应时间,sql查询的次数等。
app.add_middleware(app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
))
基于starletter实现的跨域中间件,解决跨域问题。
# 后台任务
def bktask():
for i in range(10):
print(i)
time.sleep(2)
@app.get("/request05")
def request05(backtask: BackgroundTasks):
backtask.add_task(bktask)
return "hello world"
在request05方法中,接收一个BackgroundTasks后台任务列表,然后向backtask中添加后台任务,在任务添加完成后,不用等待后台任务执行完毕,就返回响应了。后台任务会继续执行直到结束。
app = FastAPI(title="1", description="2", version="3", docs_url="/mydoc")
在实例化FastApi的时候,传入参数来配置其文档信息。
title,description,version是对于api文档的描述信息,docs_url修改其文档的地址。
app.mount("/static", StaticFiles(directory="static"), name="123")
首先pip安装aiofiles,然后由fastapi导入StaticFiles,然后将静态文件的路径绑定上。其中"/static"是前缀路径,directory是静态文件所在的路径,name无所谓。
from .main import app
from fastapi.testclient import TestClient
client = TestClient(app)
def test_abc():
response = client.get("/request04")
assert response.status_code == 422
以上是单元测试文件,需要先pip安装pytest与requests。
跟着官方文档的FastApi的基础学习到这里基本上算是结束了,既然学习了,就要付诸实践,我的想法是模仿flask的教程,完成一个简单的博客系统,采用前后端分离的方式,后端使用FastApi,前端使用React框架。
当然了,官网的还有进阶的内容,也会一边学习一边更新。