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 站在以下巨人的肩膀之上
FastAPI的核心:
Starlette 负责 web 部分(Asyncio)
Pydantic 负责数据部分(类型提示)
FastApi是站在前人肩膀上,集成了多种框架的优点的新秀框架。它出现的比较晚,2018年底才发布在github上。广泛应用于当前各种前后端分离的项目开发,测试运维自动化以及微服务、大模型开发的场景中。
一、前置知识点
1.0 常见的web框架
重量级框架:追求大而全,适合开发中大型企业级项目,不适合开发小型项目,默认提供了项目结构结构,内置大量的插件便于开发者进行项目构建,运行速度比不上轻量级框架。
轻量级框架:追求小而巧,适合开发中小型项目,也可以开发企业级大项目,但是考验开发人员的代码组织能力,本身并不提供目录结构,所有的项目代码都是开发人员定制编写的。
1.1 Typing类型标注
python3.6以后新增了Python PEP484,python语法支持给变量提供类型的标注。这种标准可以让开发人员更容易的查看到当前变量值的数据类型。main.py
,代码:
from typing import Union
if __name__ == '__main__':
"""基本数据类型"""
# python<3.6
num1 = 100
content1 = "一段内容...."
hasMoney1 = True
PI1 = 3.14
# python >=3.6
num2: int = 100
content2: str = "一段内容...."
hasMoney: bool = True
PI2: float = 3.14
"""符合类型"""
"""python<3.6"""
list1 = []
list2 = [1, 2, 3, 4]
list3 = ["a", "b", "c", "d"]
list4 = ["a", True, "c", 3.5]
set1 = set()
set2 = {3, 5, 6}
set3 = {"A", "B", "C"}
set4 = {True, "B", 100}
t1 = ()
t2 = (1, 3, 5)
t3 = ("A", "B", "C")
t4 = (True, "B", 300)
d1 = {}
d2 = {"a": 100, "b": 200}
d3 = {"a": [], "b": "1200"}
# 字典的key不支持可变类型
d4 = {(1, 2): [], (3.4, 4.7): "1200"}
"""python>=3.6"""
list11: list = []
list21: list = [1, 2, 3, 4]
list22: list[int] = [1, 2, 3, 4]
list31: list = ["a", "b", "c", "d"]
list32: list[str] = ["a", "b", "c", "d"]
list41: list = ["a", True, "c", 3.5]
list42: list[any] = ["a", True, "c", 3.5]
set11: set = set()
set21: set = {3, 5, 6}
set22: set[int] = {3, 5, 6}
set31: set = {"A", "B", "C"}
set32: set[str] = {"A", "B", "C"}
set41: set = {True, "B", 100}
set42: set[any] = {True, "B", 100}
t11: tuple = ()
t21: tuple = (1, 3, 5)
t22: tuple[int, int, int] = (1, 3, 5)
t31: tuple = ("A", "B", "C")
t32: tuple[str, str, str] = ("A", "B", "C")
t41: tuple = (True, "B", 300)
t42: tuple[bool, str, int] = (True, "B", 300)
d11: dict = {}
d21: dict = {"a": 100, "b": 200}
d22: dict[str, int] = {"a": 100, "b": 200}
d31: dict = {"a": [], "b": "1200"}
d32: dict[str, any] = {"a": [], "b": "1200"}
d33: dict[str, Union[list, str]] = {"a": [], "b": "1200"}
d41: dict = {(1, 2): [], (
3.4, 4.7): "1200"} # d41: dict[Union[list[int], list[float]], Union[list, str]] = {[1, 2]: [], [3.4]: "1200"}
"""对象/类/模块"""
from datetime import datetime
date1: datetime = datetime.now()
class Humen(object):
pass
class Student(Humen):
pass
xiaoming1: object = Student()
xiaoming2: Humen = Student()
xiaoming3: Student = Student()
1.2、http协议
1. 什么是请求头请求体,响应头响应体
2. URL地址包括什么
3. get请求和post请求到底是什么
4. Content-Type是什么
1.2.1 简介
HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于万维网(WWW:World Wide Web )服务器与本地浏览器之间传输超文本的传送协议。HTTP是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展。HTTP协议工作于客户端-服务端架构为上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。
1.2.2 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连接建立和断开的消耗。
1.2.3 http请求协议与响应协议
http协议包含由浏览器发送数据到服务器需要遵循的请求协议与服务器发送数据到浏览器需要遵循的请求协议。用于HTTP协议交互的信被为HTTP报文。请求端(客户端)的HTTP报文 做请求报文,响应端(服务器端)的 做响应报文。HTTP报文本身是由多行数据构成的字文本。
一个完整的URL包括:协议、ip、端口、路径、参数
例如: https://www.baidu.com/s?wd=moluo 其中https是协议,www.baidu.com 是IP,端口默认80,/s是路径,参数是wd=moluo
请求方式: get与post请求
- GET提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连,如EditBook?name=test1&id=123456. POST方法是把提交的数据放在HTTP包的请求体中.
- GET提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST方法提交的数据没有限制
响应状态码:状态码的职责 是当客户端向服务器端发送请求时, 返回的请求 结果。借助状态码,用户可以知道服务器端是正常 理了请求,还是出 现了 。状态码如200 OK,以3位数字和原因 组成。
1.3、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接口的设计风格,尤其适用于前后端分离的应用模式中。
关键:面向资源开发
这种RESTful风格的理念认为后端开发任务就是提供数据的,对外提供的是数据资源的访问接口,所以在定义接口时,客户端访问的URL路径就表示这种要操作的数据资源。
而对于数据资源分别使用POST、DELETE、GET、UPDATE等请求动作来表达对数据的增删查改。
请求方法代表动作 | 请求地址代表资源 | 服务端要进行的操作 |
---|---|---|
POST | /student/ | 增加学生 |
GET | /student/ | 获取所有学生 |
GET | /student/1 | 获取id为1的学生 |
PUT | /student/1 | 修改id为1的学生 |
DELETE | /student/1 | 删除id为1的学生 |
DELETE | /student/ | 删除所有学生 |
restful规范是一种通用的规范,不限制语言和开发框架的使用。事实上,我们可以使用任何一门编程语言,任何一个开发框架都可以实现符合restful规范的API接口。
二、快速入门
FastAPI官方文档:https://fastapi.tiangolo.com/zh/tutorial/
FastAPI代码仓库:https://github.com/tiangolo/fastapi
2.1 安装
强烈建议学习过程中,使用Linux系统学习,终端下使用以下命令安装FastAPI框架的核心模块:
pip install fastapi -i https://pypi.tuna.tsinghua.edu.cn/simple
确认是否安装成功:
pip freeze | grep fastapi
# window下:
pip freeze | findstr fastapi
2.2 快速体验
编写一个python作为项目访问接口,main.py
,代码:
# 1. 导入核心模块
from fastapi import FastAPI
from fastapi.responses import JSONResponse
# 2. 创建web应用程序的实例对象
app = FastAPI()
# 3. 创建一个函数,提供给外界使用,这就是API接口
async def main():
data = {"name": "xiaoming", "age": 17}
return JSONResponse(data)
# 4. 绑定url和函数,允许外界通过url地址访问上面的函数
app.add_api_route(path="/", endpoint=main, methods=["GET"])
启动运行FastAPI项目,我们提供一个web服务器,实现网络通信,允许客户端通过http协议访问项目,需要安装uvicorn,代码:
# ASGI web 服务器
pip install uvicorn -i https://pypi.tuna.tsinghua.edu.cn/simple
安装完成以后,直接入口文件中,直接调用uvicorn的测试服务器启动项目即可,main.py
,代码:
# 1. 导入核心模块
from fastapi import FastAPI
from fastapi.responses import JSONResponse
# 2. 创建web应用程序的实例对象
app = FastAPI()
# 3. 创建一个函数,提供给外界使用,这就是API接口
async def main():
data = {"name": "xiaoming", "age": 17}
return JSONResponse(data)
# 4. 绑定url和函数,允许外界通过url地址访问上面的函数
app.add_api_route(path="/", endpoint=main, methods=["GET"])
if __name__ == '__main__':
# 5. 启动项目
import uvicorn
# uvicorn.run("访问入口的模块名:实例对象", host="0.0.0.0", port=端口, reload=True)
uvicorn.run("main:app", reload=True)
执行效果:
2.3 运行项目
除了上面在代码使用uvicorn.run()
启动项目以外,还可以在终端下使用命令来启动项目(这种启动项目的方式是在工作中公司的项目正式上线以后,必须使用的启动方式),Terminal
,命令:
# 使用cd切换工作路径到入口文件处
cd xxx
uvicorn main:app --reload # main就是入口文件main.py,app就是main.py中FastAPI类实例化后的对象名
执行效果:
上面的运行操作,是使用了默认地址和端口来启动项目,也可以监听其他的地址和端口。
uvicorn main:app --host=0.0.0.0 --port=8888 --reload
执行效果:
2.4 绑定路由
main.py
,代码:
# 1. 导入核心模块
from fastapi import FastAPI
# 2. 创建web应用程序的实例对象
app = FastAPI()
# 3. 创建一个函数,提供给外界使用,这就是API接口
@app.get("/")
async def main():
data = {"name": "xiaoming", "age": 145}
return data
if __name__ == '__main__':
# 5. 启动项目
import uvicorn
# uvicorn.run("访问入口的模块名:实例对象", host="0.0.0.0", port=端口, reload=True)
uvicorn.run("main:app", reload=True)
在上面,不管使用了app.add_api_router()
还是使用app.get()
来绑定访问接口的地址,这个过程都是FastAPI中的路由组件(routers)所完成的。
路由(Router)
路由(Router),实际上是一种资源绑定的映射关系,在FastAPI中,路由就是让用户可以通过http请求与url地址来访问接口函数的工具类。
我们可以通过以下代码查看FastAPI中一共有多少接口被注册到路由中了。
# 1. 导入核心模块
from fastapi import FastAPI
# 2. 创建web应用程序的实例对象
app = FastAPI()
# 3. 创建一个函数,提供给外界使用,这就是API接口
@app.get("/")
async def main():
data = {"name": "xiaoming", "age": 145}
return data
# 查看当前项目中已经注册路由的
print(app.routes)
"""
[
Route(path='/openapi.json', name='openapi', methods=['GET', 'HEAD']), # 原生API接口的数据显示风格
Route(path='/docs', name='swagger_ui_html', methods=['GET', 'HEAD']), # swagger_ui风格的API接口文档
Route(path='/docs/oauth2-redirect', name='swagger_ui_redirect', methods=['GET', 'HEAD']), #
Route(path='/redoc', name='redoc_html', methods=['GET', 'HEAD']), # redoc