[转]fastapi框架
fastapi
,一个用于构建 API 的现代、快速(高性能)的web框架。
fastapi
是建立在Starlette和Pydantic基础上的,Pydantic是一个基于Python类型提示来定义数据验证、序列化和文档的库。Starlette是一种轻量级的ASGI框架/工具包,是构建高性能Asyncio服务的理性选择。
快速:可与 NodeJS 和 Go 比肩的
极高性能
(归功于Starlette
和Pydantic
),是最快的 Python web 框架之一。高效编码:提高功能开发速度约 200% 至 300%。
更少bug:减少约 40% 的人为(开发者)导致错误。
智能:极佳的编辑器支持。处处皆可自动补全,减少调试时间。
简单:设计的
易于使用和学习
,阅读文档的时间更短。简短:使代码重复最小化。通过不同的参数声明实现丰富功能。
健壮:生产可用级别的代码。还有
自动生成的交互式文档
。
依赖:Python 3.6 及更高版本,FastAPI 站在以下巨人的肩膀之上
View Code
FastApi是站在前人肩膀上,集成了多种框架的优点的新秀框架。它出现的比较晚,2018年底才发布在github上。广泛应用于当前各种前后端分离的项目开发,测试运维自动化以及微服务的场景中。
一、预备知识点
1.1、http协议
1. 什么是请求头请求体,响应头响应体
2. URL地址包括什么
3. get请求和post请求到底是什么
4. Content-Type是什么(前后端分离的重要请求头)
一、简介
HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于万维网(WWW:World Wide Web )服务器与本地浏览器之间传输超文本的传送协议。HTTP是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展。HTTP协议工作于客户端-服务端架构为上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。
二、 http协议特性
(1) 基于TCP/IP协议
http协议是基于TCP/IP协议之上的应用层协议。
(2) 基于请求-响应模式
HTTP协议规定,请求从客户端发出,最后服务器端响应该请求并 返回。换句话说,肯定是先从客户端开始建立通信的,服务器端在没有 接收到请求之前不会发送响应
(3) 无状态保存
HTTP是一种不保存状态,即无状态(stateless)协议。HTTP协议 自身不对请求和响应之间的通信状态进行保存。也就是说在HTTP这个 级别,协议对于发送过的请求或响应都不做持久化处理。
使用HTTP协议,每当有新的请求发送时,就会有对应的新响应产 生。协议本身并不保留之前一切的请求或响应报文的信息。这是为了更快地处理大量事务,确保协议的可伸缩性,而特意把HTTP协议设计成 如此简单的。
(4) 短连接
HTTP1.0默认使用的是短连接。浏览器和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。
HTTP/1.1起,默认使用长连接。要使用长连接,客户端和服务器的HTTP首部的Connection都要设置为keep-alive,才能支持长连接。
HTTP长连接,指的是复用TCP连接。多个HTTP请求可以复用同一个TCP连接,这就节省了TCP连接建立和断开的消耗。
三、http请求协议与响应协议
http协议包含由浏览器发送数据到服务器需要遵循的请求协议与服务器发送数据到浏览器需要遵循的请求协议。用于HTTP协议交互的信被为HTTP报文。请求端(客户端)的HTTP报文 做请求报文,响应端(服务器端)的 做响应报文。HTTP报文本身是由多行数据构成的字文本。

一个完整的URL包括:协议、ip、端口、路径、参数
例如: https://www.baidu.com/s?wd=yuan 其中https是协议,www.baidu.com 是IP,端口默认80,/s是路径,参数是wd=yuan
请求方式: get与post请求
GET提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连,如EditBook?name=test1&id=123456. POST方法是把提交的数据放在HTTP包的请求体中.
GET提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST方法提交的数据没有限制
响应状态码:状态码的职 是当客户端向服务器端发送请求时, 返回的请求 结果。借助状态码,用户可以知道服务器端是正常 理了请求,还是出 现了 。状态码如200 OK,以3位数字和原因 组成。
Content-Type是什么(前后端分离的重要请求头):
(内容类型,请求的内容类型和响应的内容渲染时的类型)
content-type:application/json 在请求头里 设定请求参数的格式(json)
content-type:application/x-www-form-urlencoded form表单数据被编码为key/value格式发送到服务器
content-type:text/html : HTML格式
content-type:text/plain :纯文本格式
等等。。。
1.2、api接口
在开发Web应用中,有两种应用模式:
-
前后端不分离[客户端看到的内容和所有界面效果都是由服务端提供出来的。]
-
前后端分离【把前端的界面效果(html,css,js分离到另一个服务端,python服务端只需要返回数据即可)】
前端形成一个独立的网站,服务端构成一个独立的网站
应用程序编程接口(Application Programming Interface,API接口),就是应用程序对外提供了一个操作数据的入口,这个入口可以是一个函数或类方法,也可以是一个url地址或者一个网络地址。当客户端调用这个入口,应用程序则会执行对应代码操作,给客户端完成相对应的功能。
当然,api接口在工作中是比较常见的开发内容,有时候,我们会调用其他人编写的api接口,有时候,我们也需要提供api接口给其他人操作。由此就会带来一个问题,api接口往往都是一个函数、类方法、或者url或其他网络地址,不断是哪一种,当api接口编写过程中,我们都要考虑一个问题就是这个接口应该怎么编写?接口怎么写的更加容易维护和清晰,这就需要大家在调用或者编写api接口的时候要有一个明确的编写规范!!!
为了在团队内部形成共识、防止个人习惯差异引起的混乱,我们都需要找到一种大家都觉得很好的接口实现规范,而且这种规范能够让后端写的接口,用途一目了然,减少客户端和服务端双方之间的合作成本。
目前市面上大部分公司开发人员使用的接口实现规范主要有:restful、RPC。
REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征)性状态转移。 它首次出现在2000年Roy Fielding的博士论文中。
RESTful是一种专门为Web 开发而定义API接口的设计风格,尤其适用于前后端分离的应用模式中。
关键:面向资源开发
这种风格的理念认为后端开发任务就是提供数据的,对外提供的是数据资源的访问接口,所以在定义接口时,客户端访问的URL路径就表示这种要操作的数据资源。
而对于数据资源分别使用POST、DELETE、GET、UPDATE等请求动作来表达对数据的增删查改。
请求方法 | 请求地址 | 后端操作 |
---|---|---|
POST | /student/ | 增加学生 |
GET | /student/ | 获取所有学生 |
GET | /student/1 | 获取id为1的学生 |
PUT | /student/1 | 修改id为1的学生 |
DELETE | /student/1 | 删除id为1的学生 |
restful规范是一种通用的规范,不限制语言和开发框架的使用。事实上,我们可以使用任何一门语言,任何一个框架都可以实现符合restful规范的API接口。
二、quick start
简单案例
安装
pip install fastapi
你还会需要一个 ASGI 服务器,生产环境可以使用
pip install uvicorn
代码

通过以下命令运行服务器:
uvicorn main:app --reload INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) INFO: Started reloader process [73408] INFO: Started server process [73408] INFO: Waiting for application startup INFO: Application startup complete
也可以直接运行:

(1)导入 FastAPI。 (2)创建一个 app 实例。 (3)编写一个路径操作装饰器(如 @app.get("/"))。 (4)编写一个路径操作函数(如上面的 def root(): ...) (5)定义返回值 (6)运行开发服务器(如 uvicorn main:app --reload)
此外,fastapi有着非常棒的交互式 API 文档,这一点很吸引人。
跳转到
案例:

三、路径操作
(学习web框架顺序:路由 路由函数(请求响应)数据库(orm 模版引擎 中间件))
路径操作装饰器
fastapi支持各种请求方式:
@app.get() @app.post() @app.put() @app.patch() @app.delete() @app.options() @app.head() @app.trace()
04 路径操作装饰器方法.py

路径操作装饰器参数:

05 路径操作装饰器方法的参数.py

include_router
将不同子应用路由做一个分发和解藕
main.py:

与main.py同级目录apps不同子应用:
# __init__.py from .app01 import app01 from .app02 import app02
app01.py:

app02.py:

四、请求与响应
main.py:

4.1、路径参数
(1)基本用法
以使用与 Python 格式化字符串相同的语法来声明路径"参数"或"变量":
@app.get("/user/{user_id}") def get_user(user_id): print(user_id, type(user_id)) return {"user_id": user_id}
路径参数 user_id
的值将作为参数 user_id
传递给你的函数。
(2)有类型 的路径参数(type hints)
你可以使用标准的 Python 类型标注为函数中的路径参数声明类型。
@app.get("/user/{user_id}") def get_user(user_id: int): print(user_id, type(user_id)) return {"user_id": user_id}
在这个例子中,user_id
被声明为 int
类型。
这将为你的函数提供编辑器支持,包括错误检查、代码补全等等。
(3)注意顺序
在创建路径操作时,你会发现有些情况下路径是固定的。
比如 /users/me
,我们假设它用来获取关于当前用户的数据.
然后,你还可以使用路径 /user/{username}
来通过用户名 获取关于特定用户的数据。
由于路径操作是按顺序依次运行的 匹配成功后就不会往下在走了,你需要确保路径 /user/me
声明在路径 /user/{username}
之前:
@app.get("/user/me") async def read_user_me(): return {"username": "the current user"} @app.get("/user/{username}") async def read_user(username: str): # username: str ,useername是str类型实例化的一个对象 在此是个参数 return {"username": username}
否则,/user/{username}
的路径还将与 /user/me
相匹配,"认为"自己正在接收一个值为 "me"
的 username
参数 所以/user/me会永远不会执行被覆盖掉了。
app01.py

4.2、查询参数(请求参数)
路径函数中声明不属于路径参数的其他函数参数时,它们将被自动解释为"查询字符串"参数,就是 url问号? 之后用&
分割的 key-value 键值对。
@app.get("/jobs/{kd}") async def search_jobs(kd: str, city: Union[str, None] = None, xl: Union[str, None] = None): # 有默认值即可选,否则必选 if city or xl: return {"kd": kd, "city": city, "xl": xl} return {"kd": kd}
在这个例子中,函数参数 city
和xl
是可选的,并且默认值为 None
。

自python3.5开始,PEP484为python引入了类型注解(type hints),typing的主要作用有:
类型检查,防止运行时出现参数、返回值类型不符。
作为开发文档附加说明,方便使用者调用时传入和返回参数类型。
模块加入不会影响程序的运行不会报正式的错误,pycharm支持typing检查错误时会出现黄色警告。
type hints
主要是要指示函数的输入和输出的数据类型,数据类型在typing 包中,基本类型有str list dict等等,
Union 是当有多种可能的数据类型时使用,比如函数有可能根据不同情况有时返回str或返回list,那么就可以写成Union[list, str] Optional 是Union的一个简化, 当 数据类型中有可能是None时,比如有可能是str也有可能是None,则Optional[str], 相当于Union[str, None]
4.3、请求体数据
当你需要将数据从客户端(例如浏览器)发送给 API 时,你将其作为「请求体」发送。请求体是客户端发送给 API 的数据。响应体是 API 发送给客户端的数据。
FastAPI 基于 Pydantic
,Pydantic
主要用来做类型强制检查(校验数据)。前端数据传到数据库时一定有数据校验,不符合类型要求就会抛出异常。
对于 API 服务,支持类型检查非常有用,会让服务更加健壮,也会加快开发速度比drf快10倍,因为开发者再也不用自己写一行一行的做类型检查。
安装上手pip install pydantic

知识点:数据校验,格式自动转化,默认值和范围约束,类型嵌套
通过postman将数据按照User类型格式 请求传入到程序中,程序会自动校验,打印请求结果如下。如果传入的格式不对,程序会自动进行数据格式转化成对的,再如果传入的数据格式没法转换此时才会报错
测试结果:(请求体内容和格式:User类型下的属性格式)

Data类型:

必填和选填的区别:和声明查询参数时一样,当一个模型属性具有默认值时,它不是必需的。否则它是一个必需属性(前端必须得传此字段(属性))。将默认值设为 None
可使其成为可选属性。
FastAPI 会自动将定义的模型类转化为JSON Schema
,Schema 成为 OpenAPI 生成模式的一部分,并显示在 API 交互文档中,查看 API 交互文档如下,该接口将接收application/json
类型的参数。
FastAPI 支持同时定义 Path 参数、Query 参数和请求体参数,FastAPI 将会正确识别并获取数据。
参数在 url 中也声明了,它将被解释为 path 参数
参数是单一类型(例如int、float、str、bool等),它将被解释为 query 参数
参数类型为继承 Pydantic 模块的
BaseModel
类的数据模型类,则它将被解释为请求体参数
4.4、form表单数据
在 OAuth2 规范的一种使用方式(密码流)中,需要将用户名、密码作为表单字段发送,content-type:application/x-www-form-urlencoded ,form表单数据被编码为key/value格式发送到服务器,而不是 JSON。
FastAPI 可以使用Form组件来接收表单数据,需要先使用pip install python-multipart
命令进行安装。
pip install python-multipart


4.5、文件上传
给服务器上传文件
请求体的新格式:Content-Type: multipart/form-data;

4.6、Reqeust对象
有些情况下我们希望能直接访问Request对象。例如我们在路径操作函数中想获取客户端的IP地址,需要在函数中声明Request类型的参数,FastAPI 就会自动传递 Request 对象给这个参数,我们就可以获取到 Request 对象及其属性信息,例如 header、url、cookie、session 等。

4.7、请求静态文件
在 Web 开发中,需要请求很多静态资源文件(不是由服务器生成的文件),如 css/js 和图片文件等。
需要服务器产生的文件是动态文件。
main.py

4.8、响应模型相关参数
(1)response_model(控制响应体结构)
前面写的这么多路径函数最终 return 的都是自定义结构的字典,FastAPI 提供了 response_model 参数(路径操作上的参数),声明 return 响应体的模型
# 路径操作 @app.post("/items/", response_model=Item) # 路径函数 async def create_item(item: Item): ...
response_model 是路径操作的参数,并不是路径函数的参数哦
FastAPI将使用response_model
进行以下操作:
-
将输出数据转换为response_model中声明的数据类型。
-
验证数据结构和类型
-
将输出数据限制为该model定义的
-
添加到OpenAPI中
-
在自动文档系统中使用。
你可以在任意的路径操作中使用 response_model
参数来声明用于响应的模型
案例:
-
注册功能
-
输入账号、密码、昵称、邮箱,注册成功后返回个人信息

(2)response_model_exclude_unset
通过上面的例子,我们学到了如何用response_model控制响应体结构,但是如果它们实际上没有存储,则可能要从结果中忽略它们。例如,如果model在NoSQL数据库中具有很多可选属性,但是不想发送很长的JSON响应,其中包含默认值。
案例:

请求:http://127.0.0.1:8080/items/foo
请求只输入了items中的foo值,未设置unset参数的时候,会响应所有的需要的字段(属性)
不设置response_model_exclude_unset参数:响应所有需要默认传的Item中的字段(属性)
{ "name": "Foo", "description": null, "price": 50.2, "tax": 10.5, "tags": [] }
设置unset参数=true:只需要响应foo中传的字段(属性)
{ "name": "Foo", "price": 50.2 }
使用路径操作装饰器的 response_model
参数来定义响应模型,特别是确保私有数据被过滤掉。使用 response_model_exclude_unset
来仅返回显式设定的值。 除了response_model_exclude_unset
以外,还有response_model_exclude_defaults
和response_model_exclude_none
,我们可以很直观的了解到他们的意思,不返回是默认值的字段和不返回是None的字段。
(3)INCLUDE和EXCLUDE
# response_model_exclude 响应中排出设置的字段 @app.get("/items/{item_id}", response_model=Item, response_model_exclude={"description"}, ) async def read_item(item_id: str): return items[item_id] # response_model_include 只响应设置的字段 @app.get("/items/{item_id}", response_model=Item, response_model_include={"name", "price"}, ) async def read_item(item_id: str): return items[item_id]
五、jinja2模板(用在前后端不分离模式)
要了解jinja2,那么需要先理解模板的概念。模板在Python的web开发中⼴泛使⽤,它能够有效的将业务逻辑和页⾯逻辑分开,使代码可读性增强、并且更加容易理解和维护。 模板简单来说就是⼀个其中包涵占位变量表⽰动态的部分的⽂件,模板⽂件在经过动态赋值后,返回给⽤户。(晦涩难懂)
jinja2是Flask作者开发的⼀个模板系统,起初是仿django模板的⼀个模板引擎,为Flask提供模板⽀持,由于其灵活,快速和安全等优点被⼴泛使⽤。
在jinja2中,存在三种语法:(在模版文件中用的动态语法)
变量取值 {{ }}
控制结构 {% %}
5.1、jinja2 的变量渲染
Main.py
:

index.html:模版文件(包含占位符的html文件)

5.2、jinja2 的过滤器
变量可以通过“过滤器”进⾏修改,过滤器可以理解为是jinja2⾥⾯的内置函数和字符串处理函数。常⽤的过滤器有:
过滤器名称 | 说明 |
---|---|
capitialize | 把值的⾸字母转换成⼤写,其他⼦母转换为⼩写 |
lower | 把值转换成⼩写形式 |
title | 把值中每个单词的⾸字母都转换成⼤写 |
trim | 把值的⾸尾空格去掉 |
striptags | 渲染之前把值中所有的HTML标签都删掉 |
join | 拼接多个值为字符串 |
round | 默认对数字进⾏四舍五⼊,也可以⽤参数进⾏控制 |
safe | 渲染时值不转义 |
那么如何使⽤这些过滤器呢?只需要在变量后⾯使⽤管道(|)分割,多个过滤器可以链式调⽤,前⼀个过滤器的输出会作为后⼀个过滤 器的输⼊。
{{ 'abc'| captialize }} # Abc {{ 'abc'| upper }} # ABC {{ 'hello world'| title }} # Hello World {{ "hello world"| replace('world','yuan') | upper }} # HELLO YUAN {{ 18.18 | round | int }} # 18 <p>用户名:{{ user|upper }}</p>
5.3、jinja2 的控制结构
模版语法的控制结构是为了是否展示用,python中的控制语句是为了是否执行代码块用
5.3.1、分支控制
jinja2中的if语句类似与Python的if语句,它也具有单分⽀,多分⽀等多种结构,不同的是,条件语句不需要使⽤冒号结尾,⽽结束控制语句,需要使⽤endif关键字
{% if age > 18 %} <p>成年区</p> {% else %} <p>未成年区</p> {% endif %}
5.3.2、循环控制
jinja2中的for循环⽤于迭代Python的数据类型,包括列表,元组和字典。在jinja2中不存在while循环。
{% for book in books %} <p>{{ book }}</p> {% endfor %}
模版文件index.html:

六、ORM操作
ORM操作是通过用python类和对象来模拟sql的原生操作
在大型的web开发中,我们肯定会用到数据库操作,那么FastAPI也支持数据库的开发,你可以用 PostgreSQL、MySQL、 SQLite Oracle 等。本文用SQLite为例。我们看下在fastapi是如何操作设计数据库的。
fastapi是一个很优秀的框架,但是缺少一个合适的orm,官方代码里面使用的是sqlalchemy,Tortoise ORM 是受 Django 启发的易于使用的异步(asyncio) ORM (对象关系映射器),我们使用Tortoise ORM。注意在Tortoise ORM下面必须操作异步支持(async,await是一个类似组合,await在异步操作数据前加)
Tortoise ORM 目前支持以下
PostgreSQL >= 9.4(使用
asyncpg
)SQLite(使用
aiosqlite
)MySQL/MariaDB(使用
aiomysql
或使用
6.1、创建模型
以选课系统为例:
models.py

6.2、aerich迁移工具
main.py

settings.py

aerich
是一种
pip install aerich
1. 初始化配置,只需要使用一次
aerich init -t settings.TORTOISE_ORM # TORTOISE_ORM配置的位置)
初始化完会在当前目录生成一个文件:pyproject.toml和一个文件夹:migrations
pyproject.toml
:保存配置文件路径,低版本可能是aerich.ini
migrations
:存放迁移文件
2. 初始化数据库,一般情况下只用一次
aerich init-db
此时数据库中就有相应的表格
如果
TORTOISE_ORM
配置文件中的models
改了名,则执行这条命令时需要增加--app
参数,来指定你修改的名字
3. 更新模型并进行迁移
修改model类,重新生成迁移文件,比如添加一个字段

迁移文件名的格式为 {version_num}{datetime}{name|update}.json。
注意,此时sql并没有执行,数据库中admin表中没有xxx字段
4. 重新执行迁移,写入数据库
aerich upgrade
5. 回到上一个版本
aerich downgrade
6. 查看历史迁移记录
aerich history
6.3、api接口与restful规范
api接口
应用程序编程接口(Application Programming Interface,API接口),就是应用程序对外提供了一个操作数据的入口,这个入口可以是一个函数或类方法,也可以是一个url地址或者一个网络地址。当客户端调用这个入口,应用程序则会执行对应代码操作,给客户端完成相对应的功能。
当然,api接口在工作中是比较常见的开发内容,有时候,我们会调用其他人编写的api接口,有时候,我们也需要提供api接口给其他人操作。由此就会带来一个问题,api接口往往都是一个函数、类方法、或者url或其他网络地址,不管是哪一种,当api接口编写过程中,我们都要考虑一个问题就是这个接口应该怎么编写?接口怎么写的更加容易维护和清晰,这就需要大家在调用或者编写api接口的时候要有一个明确的编写规范!!!
restful规范
为了在团队内部形成共识、防止个人习惯差异引起的混乱,我们都需要找到一种大家都觉得很好的接口实现规范,而且这种规范能够让后端写的接口,用途一目了然,减少客户端和服务端双方之间的合作成本。
目前市面上大部分公司开发人员使用的接口实现规范主要有:restful、RPC。
REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”或“表现层状态转化”。
简单来说,REST的含义就是客户端与Web服务器之间进行交互的时候,使用HTTP协议中的4个请求方法代表不同的动作。
GET用来获取资源
POST用来新建资源
PUT用来更新资源
DELETE用来删除资源。
只要API程序遵循了REST风格,那就可以称其为RESTful API。目前在前后端分离的架构中,前后端基本都是通过RESTful API来进行交互。
例如,我们现在要编写一个选课系统的接口,我们可以查询对一个学生进行查询、创建、更新和删除等操作,我们在编写程序的时候就要设计客户端浏览器与我们Web服务端交互的方式和路径。
而对于数据资源分别使用POST、DELETE、GET、UPDATE等请求动作来表达对数据的增删查改。
GET | /students | 获取所有学生 |
---|---|---|
请求方法 | 请求地址 | 后端操作 |
GET | /students | 获取所有学生 |
POST | /students | 增加学生 |
GET | /students/1 | 获取编号为1的学生 |
PUT | /students/1 | 修改编号为1的学生 |
DELETE | /students/1 | 删除编号为1的学生 |
6.4、选课系统接口开发
main.py:

先把接口定义出来:

然后在根据具体业务开发接口:
api/student.py

templates:

七、中间件与CORS
7.1、中间件
你可以向 FastAPI 应用添加中间件.
"中间件"是一个函数,它在每个请求被特定的路径操作处理之前,以及在每个响应之后工作.(每个请求都要登录验证)
如果你使用了
yield
关键字依赖, 依赖中的退出代码将在执行中间件后执行.如果有任何后台任务(稍后记录), 它们将在执行中间件后运行.
要创建中间件你可以在函数的顶部使用装饰器 @app.middleware("http")
.
一个中间件(就是个函数)包含两部分一个是请求代码块一个是响应代码块。
中间件参数接收如下参数:
request
.一个函数
call_next
,它将接收request,作为参数.
这个函数将
request
传递给相应的 路径操作.然后它将返回由相应的路径操作生成的
response
.然后你可以在返回
response
前进一步修改它.

7.2、CORS
跨域请求和同源请求
模拟跨域


posted on 2025-07-02 22:55 freeliver54 阅读(31) 评论(0) 收藏 举报