FastAPI框架学习笔记(一)

一 背景

出入学习,简单介绍一下FastApi这个框架,它是具备高性能,支持异步的基于OpenApi和json scheme 协议模式的并适用于API快速开发的框架。其性能可媲美GO的web框架。是python的web框架速度最快框架之一。

声明:之前学习了DRF用于开发API的框架,所以现在学习FastAPI的过程中我有时也会对比思考两框架的相同点和不同点,这样更易于理解两个框架。

学习文档来源:官方文档


二 安装

创建并进入虚拟环境

1
2
3
4
5
pip install fastapi

pip install uvicorn # python的asgi协议的 服务器



**三 运行服务器 **

在命令行输入

1
2
uvicorn main:app --reload  # reload只使用开发模式,保存代码后自动刷新服务器


四 学习之旅

1.自动生成API文档

阅读官方文档的起步部分,给我印象最深得高效的就是自动对接了swagger的文档和redoc文档,以及之后的一些教程,文档中都会强调文档的重要性,而DRF在文档生成这块做的并不是特别好,官方文档中并没有明确指出与其搭配的最佳文档,我之前也在drf中加入了swaggerui,但是生成的文档与fastapi生成的文档相比,显然后者更加全面。


1.参数传递

fastapi中视图函数中有三种参数类型,分别为了路径参数,请求体参数,查询参数。

(1)路径参数:

格式:/home/{home-id}/,这里的home-id就属于home-id,当然可以在在前面添加类型限制,当然这种写法在django和flask等其他框架中都可以这样写,不过这里fastapi区别于其他的在于它可以在参数中定义类型,用于数据验证和类型检查,这一点想比于DRF我认为使用起来简易,易于快速开发。

这里举个路径参数的例子:

1
2
3
4
# 路由参数匹配任何的路径
@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
return {"file_path": file_path}

(2) 请求体参数:

一般发生在post,put,patch请求中,格式:/home/,数据以报文段的形式放在请求体中,这里先需要转入另一个话题,pydantic的model,初识pydantic的model,给我的感觉就是挺像DRF的序列化器,但又存在不同。

DRF的序列化器和Pydantic的BaseModel的异同点:

1.相同点:

(1)两者中每个属性都可以为一个字段,都是一个对象。每个字段都可以嵌套其他的序列化器/model。

(2)前端传过来的数据/响应回去的数据都通过序列化器/model,进行反序列化和序列化。

2.不同点:

(1)DRF自身的界定是作为一个开发API框架潜入到Django,因此它要与Django继承,必然API的一系列操作要与Django存在着耦合性。而FastApi作为一款纯API开发的异步框架,自然其主导核心在API上,所以视图函数和数据的序列化和反序列化写到一起,而使用装饰器大大降低了数据验证和业务逻辑之间的耦合性。

(2)DRF的序列化器其中封装了很多功能,同时需要需要手动调用is_valid()方法,进行数据的验证。而FastApi会在装饰器内进行数据的验证,通过指明参数的类型。一旦验证出错就会抛出异常,这样 raise HTTPException异常,FastApi就会捕捉进行处理响应。

(3)FastApi的数据校验通常在继承modelbase的类(通过Field函数设定)或者装饰器(控制响应数据)以及试图函数中(参数类型)进行约束。而DRF中主要的验证是在field_validate,各字段的validator,validate中进行验证。其主要在序列化器中。

(4)FastApi主要的数据校验直接通过Python的Typing类中的类型进行,而DRF的数据校验通过封装的一系列序列化字段进行。

(5)FastApi中验证错误会返回422,表示Unprocessable Entity 请求格式正确,但其中的数据语法错误。而DRF中会验证错误,抛出ValidationErro异常后,会返回400的状态码。

(6)FastApi中的请求体参数从视图函数的参数中传递过来,而DRF是在二次封装的在dispatch方法的initial_request函数中封装到request.data的property的只读特性中。

(7)总的给我的感觉,用FastApi写Api比DRF效率快,且性能高,毕竟人家FastApi是异步的~


注意:以上的区别点不包括各个FastApi的特殊类—-Query/Body/Form/File/Path/Field的比较,这些介绍放在以后的笔记中!


下面举一些例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Item(BaseModel):
"""
用于数据校验,自动化完成
"""
name: str
price: float
is_offer: Optional[bool] = None


@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
"""
item在进入函数前已经完成相关的校验了
item具备代码补全,数据验证
"""
return {"item_name": item.name, "item_id": item_id}

说明:

其中的参数中设定的item的类型为Item,因为Item继承了BaseModel,所以它就可以被看作是一个请求体,前端的数据会通过序列化+验证==>item对象,然后被编译器识别,提供代码补全,此时就可以通过item对象来访问里面的属性了。这个例子只是简单介绍了请求时的情况,响应时的情况将在以后的笔记中记录。


1
2
3
4
5
6
7
8
9
10
11
12
13
# 创建一个枚举类,用于约束某个字段输入的数据
class ModelName(str, Enum):
name = 'syz'
age = 18
hobby = 'play game'


@app.get("/model/{model_name}")
async def get_model(model_name: ModelName):
"""只有经过枚举类型的验证才会进入该函数内"""
if model_name == ModelName.name:
return {"model_name": model_name}
return {"model_name": '???'}

1
2
3
4
5
6
7
8
9
# 需要将固定的路由放在前面
@app.get("/users/me")
async def read_user_me():
return {"user_id": "the current user"}


@app.get("/users/{user_id}")
async def read_user(user_id: str):
return {"user_id": user_id}

(3)查询参数

查询参数一般在前端传递的格式为/?page=1&limit=10,其中page和limit就是查询参数。
同样的与DRF相比较一下

1.相同点:

(1)一般用于GET方法,遵循查询参数的一系列格式

2.不同点:

(1)FastApi的查询参数放在视图参数中,而DRF的查询参数通过中间件封装在request.GET中。


注意:以上的区别点不包括各个FastApi的特殊类—-Query/Body/Form/File/Path/Field的比较,这些介绍放在以后的笔记中!


举个例子:

1
2
3
4
5
6
7
8
# 路径参数 + 路径参数 + 查询参数,顺序不能乱!

@app.put("/items_two/{item_id}")
async def create_item(item_id: int, item: Item, q: Optional[str] = None):
result = {"item_id": item_id, **item.dict()}
if q:
result.update({"q": q})
return result

总结

这篇笔记在学习到FastApi后半部分时候记录的笔记,因为有太多要讲,但一次很难讲完全部,因此我拆分几部分进行记录讲解。下一篇笔记准备讲解记录FastApi中的特殊类!

总的来说,FastApi写API给我的感觉很舒服,完备的SwaagerUi文档,数据校验的优雅代码,希望之后的学习中FastApi可以带给我更多惊喜,提高Api的急速开发!