【FastAPI】从0开始学FastAPI!一篇搞懂!
FastAPI是一个现代、快速(高性能)的python web框架,基于标准的python类型提示,使用python3.6+构建的web框架。FastAPI的架构为RESTful风格,RESTful是一种网络应用程序的设计风格和开发方式,其特点为每一个URI代表一种资源,客户端通过GET、POST、PUT、DELETE等动作,对服务器端资源进行操作。
一、什么是FastAPI?
FastAPI是一个现代、快速(高性能)的python web框架,基于标准的python类型提示,使用python3.6+构建的web框架。
FastAPI的架构为RESTful风格,RESTful是一种网络应用程序的设计风格和开发方式,其特点为每一个URI代表一种资源,客户端通过GET、POST、PUT、DELETE等动作,对服务器端资源进行操作。
(一)环境准备
1.安装FastAPI:pip install fastapi
(交互文档需要访问外部资源,行内在1.0.3版本中将外部资源转为行内资源,若行内网络使用请指定版本1.0.3)
2.安装ASGI服务:pip install uvicorn
(二)入门示例
1. 新建一个main.py文件,编写如下:
from fastapi import FastAPI
app = FastAPI()
@app.get('/')
def root():
return {'message': 'Hello World'}
2. 启动服务
命令:uvicorn main:app --reload
² main:文件main.py(python模块)
² app:在模块中app=FastAPI()行中创建的对象
² --reload:代码更改后自动重启服务(上线时该参数值不能为true,降低性能)
启动服务后终端将会看到如下输出:
3. 访问服务
启动服务后,使用浏览器访问127.0.0.1:8000,
可以得到{"Hello":"World"} ,说明环境安装成功。
4. API交互文档
启动服务后,FastAPI会自动给生成两个交互文档:
SwaggerUI:http://127.0.0.1:8000/docs
ReDoc:http://127.0.0.1:8000/redoc
http://127.0.0.1:8000/openapi.json
5. 查看openapi.json
http://127.0.0.1:8000/openapi.json
6. 本地部署
命令:uvicorn main:app --host 0.0.0.0 --port 8000
² main:启动服务的py文件名
² app:服务对象名
² --host:IP地址
² --port:端口
本地部署后,可通过外部访问本地启动服务。
(三)入门示例了解FastAPI结构
from fastapi import FastAPI
app = FastAPI()
@app.get('/')
def root():
return {'message': 'Hello World'}
1. 导入FastAPI
from fastapi import FastAPI
2. 创建一个app实例
app = FastAPI()
3. 编写路径操作装饰器
@app.get('/')
(1)使用get请求,也可使用其他请求方法的装饰器
@app.post()
@app.put()
@app.delete()
@app.options()
@app.head()
@app.patch()
@app.trace()
(2)路径('/') 自定义
4. 定义路径操作函数
def root():
return {'message': 'Hello World'}
(1)python函数;
(2)当FastAPI接收一个使用GET方法访问路径为/的请求时这个函数会被调用;
(3)此处的返回值可以是:字典,列表,单独的值:比如str,int,Pydantic模型,自动转换为JSON的对象和模型。
5. 运行开发服务
uvicorn main:app
此处可以执行要运行的服务器IP和端口号,默认IP :127.0.0.1,端口:8000
uvicorn main:app --host 127.0.0.1 --port 8080
二、uvicorn启动方式
(一)使用命令行运行
按入门示例的方式,在py文件所在目录下的命令行中运行。
(二)使用uvicorn.run()
参数 | 作用 |
app | 运行的 py 文件:FastAPI 实例对象 |
host | 访问url,默认 127.0.0.1 |
port | 访问端口,默认 8000 |
reload | 热更新,为true时有内容修改自动重启服务器 |
debug | 同reload |
reload_dirs | 设置需要 reload 的目录,List[str] 类型 |
log_level | 设置日志级别,默认 info |
三、接口文档注释
(一)注释类型
1.title标题
2.description描述
3.summary注释
4.tags标签
示例:
from fastapi import FastAPI
app = FastAPI(title='测试api应用程序', description='整体描述')
@app.get(path='/', summary='接口注释', description='接口描述', tags= ['Root'])
def read_root():
return {"Hello": "World"}
(二)多行注释
1.示例1
@app.get("/list/query1")
def list_query1(page:int = 1,limit:int = 10):
"""
多行备注
- param page: 页数
- param limit: 每页个数
- return: 以给定页数和每页个数为数据的对象,字段为page,limit
"""
return
2.效果1
3.示例2
@app.get("/list/query2")
def list_query2(page:int = 1,limit :int = None):
"""
多行备注
| *参数名称* | *参数类型* | *默认值* | *释义* |
| :--: | :--: | :--: | :--: |
| page | int | 1 | 页数|
| limit | int | none | 每页个数|
| *返回值类型* | *释义* |
| :--: | :--: |
| page | 页数 |
| limit | 每页个数 |
"""
return
4.效果2
(三)路径/查询参数/请求体注释
1.示例1
from fastapi import FastAPI,Query
from .demo_data import *
app = FastAPI()
@app.get('/customer_level/{level}')
def read_enum1(Level: Cust_level = Query(Cust_level.Level1,alias="零售客户等级")):
return {'level_res': Level}
2.效果1
3.示例2
from fastapi import FastAPI,Query
app = FastAPI()
@app.get("/list/query3")
def list_query3(page:int = Query('1',alias='页数'),
limit :int = Query(...,alias='每页个数')):
return{"page":page,"limit":limit}
4.效果2
5.示例3
from fastapi import FastAPI,Body
app = FastAPI()
class Item2(BaseModel):
name:str = Body(default="张三",alias="姓名")
description:str=Body(default="",alias="描述")
price:float = Body(default='',alias="分数",gt=5.0,lt=10.5)
tax:float=None
@app04.post('
def create_item3(item:Item2 ):
return item
6.效果3
四、声明路径参数
(一)路径参数
1.代码
@app.get("/items/{item_id}")
def read_items(item_id):
return {"item_id": item_id}
2.代码运行之后,路径参数item_id的值会作为read_items函数参数item_id的值
(二)指定数据类型的路径参数
1.代码
@app.get("/item1/{item_id}")
def read_int(item_id:int): #以整数类型接受参数
return {"item_id": item_id}
@app.get("/item2/{item_id}")
def read_str(item_id:str): #以字符串类型接受参数
return {"item_id": item_id}
2.说明
如果访问链接时提供的参数类型不对,FastAPI还会自动进行数据校验。类型声明有int、str,float,bool或者其他更复杂的类型。
(三)枚举型的路径参数
1.代码
from enum import Enum
from fastapi import FastAPI
class cust_level(str, Enum):
level1 = '钻石'
level2 = '私人银行'
level3 = '金葵花'
level4 = '金卡'
level5 = '普卡'
app = FastAPI()
@app.get('/customer_level/{level}')
def read_enum(level: cust_level):
return {'level_res': level}
2.运行效果
3.说明
1. 创建一个继承str和Enum的类,并创建几个类属性,这些类属性的值将是可用的有效值。
2. 声明路径参数。路径参数level的值将传递给函数read_enum的参数Level。且这个参数的取值范围只能是Cust_level类中类属性的值。
3. read_enum函数中可以调用cust_level类的类属性。调用方式:Cust_info.Name
(四) 路径类型的路径参数
1.代码
@app.get("/files/{file_path:path}") # :path 代表任意路径
def read_file(file_path: str):
return {"file_path": file_path}
2.说明
路径参数时请求路径的一部分,如果不传,请求路径不完整则会报错404。
五、声明查询参数
(一)基础概念
1.查询参数就是在URL之后的key-value键值对每对键值对用&分割开来。
示例:
http://127.0.0.1:8000/items/?page=1&limit=10
该请求的查询参数有两个,page和limit,值分别为1,10。
2.当声明不属于路径参数的其他函数参数时,它们将自动解释为查询参数;查询参数可以是非必填,也可具有默认值。
3.查询参数也是URL的一部分,所以 “本质上” 它们都是字符串。
但当需要使用Python类型来声明query参数时(例如用int),他们就会被转换为相应的类型并且依据这个类型来验证传入参数。
(二) 设置参数默认值
1.代码
@app.get("/list/query1")
def list_query1(page: int = 1, limit: int = 10):
return {"page": page, "limit": limit}
2.调用方法
ip:port/list/query?page=1&limit=3
ip:port/list/query # 使用默认值 page=1,limit=10
ip:port/list/query?page=&limit= # 报错,报错如下爱徒
(三)设置为可选参数
声明可选的查询参数,只需要将他们的默认值设置为None即可。
1.代码
@app.get("/list/query2")
def list_query2(page:int = None):
return {"page": page}
2.调用方法
ip:port/list/test
ip:port/list/test?page=3
(四)设置为枚举类型参数
1.代码
from fastapi import APIRouter,Query
from enum import Enum
//自定义枚举类
class Cust_level(str, Enum):
Level1 = '钻石'
Level2 = '私人银行'
Level3 = '金葵花'
Level4 = '金卡'
Level5 = '普卡'
app03 = APIRouter()
@app03.get("/list/query4")
def list_query4(name:str = Query(...,alias='姓名'),
level :Cust_level = Cust_level.Level1 ):
return{"name":name,"level":level}
2.效果
5. Query简单介绍
Query库,可以提供对查询参数进行额外校验的功能。
1. Query函数的入参值说明
def Query( # noqa: N802
default: Any = Undefined, //参数类型,传...表示为必传,传None表示为可选
*,
alias: Optional[str] = None, //别名
title: Optional[str] = None,
description: Optional[str] = None, //描述
gt: Optional[float] = None,
ge: Optional[float] = None,
lt: Optional[float] = None,
le: Optional[float] = None,
min_length: Optional[int] = None, //最小长度
max_length: Optional[int] = None, //最大长度
regex: Optional[str] = None, //正则表达式校验
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
deprecated: Optional[bool] = None,
include_in_schema: bool = True,
**extra: Any,
) -> Any:
2. 最小长度、最大长度、正则表达式校验
from fastapi import FastAPI, Query
app = FastAPI()
@app.get('/items/')
def read_items(
q: str = Query(None, min_length=1, max_length=20, regex='^str$') ):
results = {'items': [{'item_id': 'Foo'}, {'item_id': 'Bar'}]}
if q:
results.update({'q': q})
return results
这个特定的正则表达式检查接收到的参数值:
^: 表示字符串str前面没有字符
str: 匹配 str字符串
$: 表示字符串str后面不匹配任何字符
3. 数值校验
(1)大于等于ge,只能比较整数,例如大于等于1。
from fastapi import FastAPI, Path
app = FastAPI()
@app.get('/items/{item_id}')
def read_items(
*, item_id: int = Path(..., ge=1), q: str ):
results = {'item_id': item_id}
if q:
results.update({'q': q})
return results
(2)小于等于le,只能比较整数,例如小于等于1000。
from fastapi import FastAPI, Path
app = FastAPI()
@app.get('/items/{item_id}')
async def read_items(*,
*,item_id: int = Path(..., gt=0, le=1000), q: str ):
results = {'item_id': item_id}
if q:
results.update({'q': q})
return results
(3)大于gt, 小于lt,可以比较浮点数和整数。
from fastapi import FastAPI, Path, Query
app = FastAPI()
@app.get('/items/{item_id}')
def read_items(
*, item_id: int = Path(..., ge=0, le=1000),
item_id: int = Path(..., ge=0, le=1000),
q: str,
size: float = Query(..., gt=0, lt=10.5) ):
results = {'item_id': item_id}
if q:
results.update({'q': q})
return results
六、请求体
(一) 基础概念
请求体是客户端发送到API的数据,响应体是API发送给客户端的数据。
定义请求体,一般需要使用Pydantic模型,发送请求体数据,常用以下几种方法:POST(最常见)、PUT、DELETE、PATCH。
(二)不使用Pydantic模型
1.代码
@app.post("/bodyapi1")
def read_item1(item: dict):
return {"item": item}
指定查询参数的类型为dict
2.效果
{
"item":1234
}
(三)使用Pydantic模型
1. 如何定义请求体
A:从pydantic中导入BaseModel
from pydantic import BaseModel
B:创建请求体数据模型
声明请求体数据模型为一个类,且该类继承 BaseModel。所有的属性都用标准Python类。
和查询参数一样:数据类型的属性如果不是必须的话,可以拥有一个默认值或者是可选None,否则,该属性就是必须的。
from pydantic import BaseModel
class Item(BaseModel): //自定义一个pydantic模型
name: str
description: str = None
price: float
tax: float = None
所以访问链接的时候传入的请求体可以是下面两种:
{
'name': 'Foo',
'description': 'An optional description',
'price': 45.2,
'tax': 3.5
}
另一种可以不传递默认值或者是可选值:
{
'name': 'Foo',
'price': 45.2
}
C: 属性类型自动转换为相应的请求类型
D: 验证数据失败,会返回清晰的报错
2.使用数据模型
A:将模型定义为参数
将上面定义的模型添加到你的路径操作中,类比定义Path和Query参数的方式,声明参数的类型创建的模型Item:
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
app = FastAPI()
@app.post('/body/')
def create_item(item: Item):
return item
B:在函数内部,可以直接访问模型对象的所有属性
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
app = FastAPI()
@app.post('/items/')
async def create_item(item: Item):
return Item.name
关注:
可以同时定义路径参数、查询参数、请求体参数,如果path中声明了某个参数,那么这个参数将作为路径参数是使用;如果参数是单一类型(例如int,float,str,str,bool等),它将被解释为query参数。
C:多个模型的请求体参数
我们可以定义多个请求体模型。例如:Item和User
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
class User(BaseModel):
username: str
full_name: str = None
@app.put('/items/{item_id}')
async def update_item(*, item_id: int, item: Item, user: User):
results = {'item_id': item_id, 'item': item, 'user': user}
return results
在这种情况下,请求体输入的格式是一个字典,它将使用参数名称作为正文中的key,Pydantic的类作为key的内容,例如上述请求体的正确格式如下:
{
'item': {
'name': 'Foo',
'description': 'The pretender',
'price': 42.0,
'tax': 3.2
},
'user': {
'username': 'dave',
'full_name': 'Dave Grohl'
}
}
我们将这个输入传入之后得到的输出如下。URL为:127.0.0.1:8001/items/3
{
'item_id': 3,
'item': {
'name': 'Foo',
'description': 'The pretender',
'price': 42.0,
'tax': 3.2
},
'user': {
'username': 'dave',
'full_name': 'Dave Grohl'
}
}
(四)Body简单介绍
1.可以将单类型的参数成为Request Body的一部分,即查询参数变成请求体参数。
2.和 Query提供的额外校验基本一致。
def Body( # noqa: N802
default: Any = Undefined,
*,
embed: bool = False,
media_type: str = "application/json",
alias: Optional[str] = None,
title: Optional[str] = None,
description: Optional[str] = None,
gt: Optional[float] = None,
ge: Optional[float] = None,
lt: Optional[float] = None,
le: Optional[float] = None,
min_length: Optional[int] = None,
max_length: Optional[int] = None,
regex: Optional[str] = None,
example: Any = Undefined,
examples: Optional[Dict[str, Any]] = None,
**extra: Any,
) -> Any:
以上就是本次分享的内容,感谢大家支持。您的关注、点赞、收藏是我创作的动力。
万水千山总是情,点个 👍 行不行。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)