FastApi 一个用于构建API的现代的,快速(高性能)的web框架
fastapi 是建立在Starlette 和Pydantic的基础之上的,Starlette 一个轻量级的ASGI框架和工具包,是构建高性能Asyncio服务的理性选择;Pydantic,一个基于python类型提示来定义数据验证,序列化和文档化的库。
一.FastAPI使用的流程:
- 导入 FastAPI。
2.创建一个 app 实例。
3.编写一个路径操作装饰器(如 @app.get("/"))
4.编写一个路径操作函数(如上面的 def root(): ...)
5.运行开发服务器(如 uvicorn main:app --reload)
a》 main:main.py 文件(一个 Python “模块”)
b》 app:在 main.py 文件中通过 app = FastAPI() 创建的对象。
c》 --reload:让服务器在更新代码后重新启动。仅在开发时使用该选项。
# 引入FastAPI类
from fastapi import FastAPI
# 实例化
app = FastAPI()
# 定义路径装饰器和路径操作函数
@app.get("/")
async def root():
return {"message": "Hello World 老高"}
# 定义路径装饰器和路径操作函数
@app.get("/hello/{name}")
async def say_hello(name: str):
return {"message": f"Hello {name}"}
2. 在PyCharm中 快捷的使用uvicorn试试服务器
from fastapi import FastAPI
import uvicorn
# 实例化
app = FastAPI()
# 定义路径装饰器和路径操作函数
@app.get("/")
async def root():
return {"message": "Hello World 老高"}
# 判断该文件是否为主运行文件。
if __name__ == "__main__":
uvicorn.run("main:app",host='127.0.0.1',port=8080,reload=True)
#uvicorn.run("文件名称:入口实例名称",host='127.0.0.1',port=8080,reload=True)
二.路径操作
1.路径装饰器
fastapi 支持各种请求方式,如下:
@app.get()
@app.post()
@app.put()
@app.patch()
@app.delete()
@app.option()
@app.head()
@app.trace()
1.主文件路由和子路由,以及APIRouter和include_router的使用
- 首先定义一个子路由。
子路由的路径为shopping.apps.app01,文件名称为urls.py
#urls.py 子路由代码如下
# 导入子路由依赖文件 APIRouter
from fastapi import APIRouter
#实例化子路由 路由接口
shop = APIRouter()
#定义子路由的 路由方法
@shop.get('/food/')
async def shop_food():
return {'shop':'food'}
@shop.get('/bed/')
async def shop_bed():
return {'shop':'bed'}
- 定义主入口文件
include_router 路由分发
# 导入主文件所需依赖
from fastapi import FastAPI
import uvicorn
# 导入子路由
from shopping.apps.app01.urls import shop
from shopping.apps.app02.urls import user
app = FastAPI()
@app.get('/')
async def index():
return '我是首页'
# 导入子路由(参数分别为 APIRouter,前缀,便签)
app.include_router(shop,prefix='/shop',tags=['购物中心接口'])
app.include_router(user,prefix='/user',tags=['用户中心接口'])
if __name__ == "__main__":
uvicorn.run("main:app",port=8080,host='127.0.0.1',reload=True)
三.数据请求与响应
1.路径参数和查询参数
- 路径参数
from fastapi import FastAPI
import uvicorn
# 实例化
app = FastAPI()
# 首页,路径参数
@app.get('/{name}/',tags=['首页接口'])
async def index(name):
return {'index':'hello ,'+name}
- 查询参数:除了路径参数以外的参数 都是查询参数。
from fastapi import FastAPI
import uvicorn
from typing import Union,Optional
# 实例化
app = FastAPI()
# 参数可以设置默认值,也可以设置数据类型,数据类型类型可以设置为多个数据类型
# Union 定义数据类型的一个集合,可以设置多个数据类型。Union[str,None] 就等于Optional['str']
@app.get('/login/{name}/',tags=['首页接口'])
async def login(name,psd:Union[str,None]=None,nick:Optional['str']=None):
return {'index':'hello ,'+name,
"昵称":nick,
"密码":psd
}
2. 请求体
请求体是客户端发送给 API 的数据。响应体是 API 发送给客户端的数据。
我们使用 Pydantic 模型来声明请求体,并能够获得它们所具有的所有能力和优点。
- 请求体不能够使用GET方法发送,只能使用POST(较常见)、PUT、DELETE 或 PATCH方法之一
- pydantic中的模块
- BaseModel:定义数据模型的基类。它允许你创建具有验证、类型转换和序列化功能的数据模型,以便于数据的输入、处理和输出。
- Field:定义模型字段(Model Field)的一个函数,Field 函数允许你为 Pydantic 模型的字段指定额外的配置选项,例如数据校验、默认值、别名等。它通常与 Pydantic 的模型类一起使用。
- validator:添加自定义验证逻辑的装饰器函数。它允许你在 Pydantic 模型中定义验证函数,以便在创建或更新模型实例时执行自定义的数据验证。
2.数据模型的使用
可以限定数据类型,可以使用默认值,可以使用正则表达式,可以多重数据类型嵌套; 还可以使用validator 装饰器,自定义自己的验证规则
# 请求体
from fastapi import APIRouter
from typing import Union,Optional,List
# 请求体 - 导入Pydantic的baseModel
from pydantic import BaseModel,Field,validator
from datetime import date #引入关于时间的类
app03 = APIRouter()
# 创建数据模型 Addr,声明为参数
class Addr(BaseModel):
province:str
city:str
# 创建数据模型 Item
class Item(BaseModel):
# name:str = Field(regex='^a') # 可以使用正则表达式进行验证
name:str
price: int = None
nick:str
age:int = Field(default=0,gt=0,lt=100) # 限定区间值,默认值为0,大于0,切小于100的区间值
description:Union[str,None] = None
tax:Union[float,None] = None #可以使用多个数据类型集合,可以设置默认值
dateDay:date = None
add:Addr # 多重数据类型嵌套
# 自定义验证规则,需要引入validator类
# 自定义name的验证规则,譬如:name 能为字母组合
@validator("name")
def name_must_alpha(cls,v):
assert v.isalpha(),"name must is alpha" # assert 断言,如果v.isalpha()为False时,然后后面提示信息
return v
# 自定义nick验证规则,昵称需要字母和数字的组合,并且必须又一个字母为大写字母
@validator('nick')
def nick_must_alpha(cls,s):
# 判断是否同时包含字母和数字
has_alpha = any(char.isalpha() for char in s)
has_digit = any(char.isdigit() for char in s)
# 判断是否包含至少一个大写字母
has_upper = any(char.isupper() for char in s)
assert has_alpha and has_digit and has_upper,'nick格式不正确 ,nick 只能是字母+数字+大写的组合'
return s
@app03.post('/items/')
async def create_item(item:Item):
return item
3. Form表单
Form 是直接继承自 Body 的类,使用 Form 可以声明与 Body (及 Query、Path、Cookie)相同的元数据和验证。
# 引入Form表单
from fastapi import APIRouter,Form
app04 = APIRouter()
# 使用Form表单的方式接受数据
@app04.post('/regin/')
async def regin_test(username:str=Form(),password:str=Form()):
print(f'username:{username};password:{password}')
return {'username':username}
与 JSON 不同,HTML 表单(<form></form>)向服务器发送数据通常使用「特殊」的编码。
表单数据的「媒体类型」编码一般为 application/x-www-form-urlencoded
但包含文件的表单编码为 multipart/form-data
注意事项:
可在一个路径操作中声明多个 Form 参数,但不能同时声明要接收 JSON 的 Body 字段。因为此时请求体的编码是 application/x-www-form-urlencoded,不是 application/json。
这不是 FastAPI 的问题,而是 HTTP 协议的规定。
4. 文件上传
因为上传文件以「表单数据」形式发送,所以接收上传文件,要预先安装 python-multipart
文件上传主要用到2个模块:File和UploadFile。
File一般只适应于小文件上传。大文件或者多文件一般使用UploadFile。
# 引入Form表单
from fastapi import APIRouter,File,UploadFile
from typing import List
import os
app05 = APIRouter()
@app05.post('/file/')
async def get_file(file:bytes = File()):
# print('get_file-->',file) # 输出为二进制数据
# 将文件一次性加载到内存,只适合小文件上传,上传大文件到时候会导致内存溢出,性能低下
return {'file':len(file)}
@app05.post('/files/')
async def get_files(files:List[bytes] = File()):
# 一次性上传多个文件
return {'files':len(files)}
@app05.post('/updateFile/')
async def get_updatefile(file:UploadFile):
# print('UploadFile-->',file) # 输出:<starlette.datastructures.UploadFile object at 0x1109ab7d0>
# 文件上传,并保存到服务器
img = os.path.join('imgs',file.filename)
with open(img,'wb') as f:
for line in file.file:
f.write(line)
return {'files':file.filename}
@app05.post('/updateFiles/')
async def get_updatefiles(files:List[UploadFile]):
# 多个文件上传,并保存到服务器
for file in files:
img = os.path.join('imgs',file.filename)
with open(img,'wb') as f:
for line in file.file:
f.write(line)
print('文件写入完成!')
return {
'files': [file.filename for file in files]
}
5. Resquest对象
Fast API 还包含着Resquest模块,Header模块,通过这个模块可以快速的访问用户的头部信息
# Request对象 可以获取一些除了数据之外的其他数据,如文件头信息,IP地址,
from fastapi import APIRouter,Request
app06 = APIRouter()
@app06.post("/get_request/")
async def get_request(res:Request):
# lst = [i for i in res]
return {
'客户端url地址':res.url,
'客户端ip地址':res.client.host,
'头信息headers':res.headers,
'cookie':res.cookies
}
6. 请求静态文件
静态文件:不是由服务器生成的文件如css,js,图片文件等等。
# 设置网站的静态路径
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
# 实例化
app = FastAPI()
# 设置静态文件路径,
app.mount('/static',StaticFiles(directory="statics"))
# 通过改地址可以直接访问statics文件夹内的文件。如css文件:http://127.0.0.1:8080/static/css/common.css
7. 响应模型的参数
FastAPI提供response_model参数,来声明return相应体的模型。response_model是「装饰器」的参数,而不是被装饰函数的参数。
FastAPI 将使用此 response_model 来:
- 将输出数据转换为其声明的类型。
- 校验数据。
- 在 OpenAPI 的路径操作中为响应添加一个 JSON Schema。
- 并在自动生成文档系统中使用。
from fastapi import APIRouter
from pydantic import BaseModel,EmailStr
# 注意如果引入EmailStr后还报错,需要安装一下
from typing import Union
app07 = APIRouter()
# 接收数据模型
class UserIn(BaseModel):
username:str
password:str
email: EmailStr
fullname:Union[str,None] = None
# 响应的数据模型
class UserOut(BaseModel):
username: str
email: EmailStr
fullname: Union[str, None] = None
# 注册功能:response_model 给装饰器添加数据模型
@app07.post("/user_reg",response_model=UserOut)
async def user_reg(user:UserIn):
return user
# 输入值模型需要有username,password,email,fullname 这几个节点。而响应数据只有username,email,fullname 三个节点。这个就是使用响应的数据模型的作用。
响应数据模型其他功能:
- response_model:返回值响应的数据模型
- response_model_exclude_unset:过滤掉响应中的默认值,而是仅有实际设置的值
- response_model_exclude_none=True: 相应值中不返回none的值
- response_model_exclude_defaults=True:相应值中返回默认值
- response_model_include: 相应值中只返回include包含的数据
- response_model_exclude:相应值中不返回include包含的数据
使用路径操作装饰器的 response_model 参数来定义响应模型,特别是确保私有数据被过滤掉。
response 其他功能demo :
#+++++++++++++++++++++++++++++++++++
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: float = 10.5
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
"baz": {
"name": "Baz",
"description": "There goes my baz",
"price": 50.2,
"tax": 10.5,
},
}
@app07.get(
"/reponse/{item_id}/name",
response_model=Item,
response_model_include={"name", "description"},
)
async def read_item_name(item_id: str):
return items[item_id]