文章目录
- 1、请求体数据
- 2、form表单数据
- 3、小文件上传
- 1.单文件上传
- 2.多文件上传
- 4、大文件上传
- 1.单文件上传
- 2.多文件上传
1、请求体数据
前面我们讲到,get请求中,我们将请求数据放在url中,其实是非常不安全的,我们更愿意将请求数据放在请求体中。
当你需要将数据从客户端(例如浏览器)发送给 API 时,你将其作为「请求体」发送。请求体是客户端发送给 API 的数据。响应体是 API 发送给客户端的数据。
FastAPI 基于 Pydantic ,Pydantic 主要用来做类型强制检查(校验数据)。不符合类型要求就会抛出异常。
对于 API 服务,支持类型检查非常有用,会让服务更加健壮,也会加快开发速度,因为开发者再也不用自己写一行一行的做类型检查。
安装上手
pip install pydantic
from fastapi import FastAPI # FastAPI 是一个为你的 API 提供了所有功能的 Python 类。
import uvicornfrom typing import Optional,Union,Listfrom pydantic import BaseModel,Fieldfrom datetime import date#创建应用程序,app是应用程序名
app = FastAPI() # 这个实例将是创建你所有 API 的主要交互对象。这个 app 同样在如下命令中被 uvicorn 所引用#fastapi要实现校验功能,需要借助pydantic这个模块,我们需要自己写个类,继承pydantic模块中的BaseModel,才能具有该功能
#在类型上做类型限制
class User(BaseModel):name:str = 'root'#默认是0,输入限制大于0,小于100age: int = Field(default=0, lt=100, gt=0)birth: Optional[date] = None#限制为数组,里面的元素限制为int类型friends: List[int] = []description: Union[str, None] = None#异步的请求参数,函数加上async
@app.post("/data") #路径参数与查询参数共存
#传参data限制为User类型
async def data(data:User):#将查询结果返回return {}if __name__ == '__main__':#注意,run的第一个参数 必须是文件名:应用程序名uvicorn.run("请求体数据:app", port=8080, reload=True)
在docs测试,可以看到请求体限制数据类型
报错排查
报错:
TypeError: Failed to execute ‘fetch’ on ‘Window’: Request with GET/HEAD method cannot have body.
有@ResponseBody才会在接口中获取swagger列表
是由于方法中申明的是get方法却用了@requestBody
故将get 请求改为post 请求即可
当传参不符合限制要求,响应失败,提示年龄应小于100
当请求体参数完全符合要求,才能正确响应
我们可以将数据返回
#fastapi要实现校验功能,需要借助pydantic这个模块,我们需要自己写个类,继承pydantic模块中的BaseModel,才能具有该功能
#在类型上做类型限制
class User(BaseModel):name:str = 'root'#默认是0,输入限制大于0,小于100age: int = Field(default=0, lt=100, gt=0)birth: Optional[date] = None#限制为数组,里面的元素限制为int类型friends: List[int] = []description: Union[str, None] = None#异步的请求参数,函数加上async
@app.post("/data") #路径参数与查询参数共存
#将传参data限制为User类型
async def data(data:User):print(data,type(data))#将查询结果返回return data
注意,当输入的数据类型跟限制类型不一致时,pydantic会尝试做数据类型转换,转换成功就可以正常返回,转换失败才报错
Field比较强大,可以做各种限制,甚至可以做正则限制 pattern
def Field( # noqa: C901default: Any = PydanticUndefined,*,default_factory: typing.Callable[[], Any] | None = _Unset,alias: str | None = _Unset,alias_priority: int | None = _Unset,validation_alias: str | AliasPath | AliasChoices | None = _Unset,serialization_alias: str | None = _Unset,title: str | None = _Unset,description: str | None = _Unset,examples: list[Any] | None = _Unset,exclude: bool | None = _Unset,discriminator: str | types.Discriminator | None = _Unset,json_schema_extra: JsonDict | typing.Callable[[JsonDict], None] | None = _Unset,frozen: bool | None = _Unset,validate_default: bool | None = _Unset,repr: bool = _Unset,init: bool | None = _Unset,init_var: bool | None = _Unset,kw_only: bool | None = _Unset,pattern: str | None = _Unset,strict: bool | None = _Unset,gt: float | None = _Unset,ge: float | None = _Unset,lt: float | None = _Unset,le: float | None = _Unset,multiple_of: float | None = _Unset,allow_inf_nan: bool | None = _Unset,max_digits: int | None = _Unset,decimal_places: int | None = _Unset,min_length: int | None = _Unset,max_length: int | None = _Unset,union_mode: Literal['smart', 'left_to_right'] = _Unset,**extra: Unpack[_EmptyKwargs],
也可以自定义一个函数做限制,使用到了pydantic里面的validator装饰器
最新版的validator已被废弃
最新版使用field_validator装饰器
#在类型上做类型限制
class User(BaseModel):name:str = 'root'#默认是0,输入限制大于0,小于100age: int = Field(default=0, lt=100, gt=0)birth: Optional[date] = None#限制为数组,里面的元素限制为int类型friends: List[int] = []description: Union[str, None] = None@field_validator('name')def validate_name(cls,v):assert v.isalpha(), 'name must be alpha'return v
校验生效
类型嵌套:
我们定义的类型,可以组合嵌套方式使用
class Data(BaseModel): # 类型嵌套
users: List[User]
@app.post(“/data/”)
async def create_data(data: Data):
# 添加数据库
return data
也可以这样嵌套,请求体数据是列表套字典形式
2、form表单数据
在 OAuth2 规范的一种使用方式(密码流)中,需要将用户名、密码作为表单字段发送,而不是 JSON。
FastAPI 可以使用Form组件来接收表单数据,需要先使用 pip install python-multipart 命令进行安装。
pip install python-multipart
from fastapi import FastAPI, Form
import uvicornapp = FastAPI()@app.post("/regin")
def regin(username: str = Form(..., max_length=16, min_length=8, pattern='[a-zA-Z]'), #Form对输入的数据可以做些限制password: str = Form(..., max_length=16, min_length=8, pattern='[0-9]')):print(f"username:{username},password:{password}")return {"username": username}if __name__ == '__main__':#注意,run的第一个参数 必须是文件名:应用程序名uvicorn.run("表单:app", port=8080, reload=True)
此时发送请求,content-type 必须是application/x-www-form-urlencoded
否则发送请求失败
使用application/x-www-form-urlencoded发送成功
3、小文件上传
文件上传,文件会放在请求体里面,但是请求头的content-type是multipart/form-data
1.单文件上传
# file: bytes = File():适合小文件上传
@app.post("/files/")
#文件时字节流类型,是fastapi里面的File类型
async def create_file(file: bytes = File()):print("file:", file)return {"file_size": len(file)}
在docs请求测试,可以看到请求的content-type是multipart/form-data
返回了图片的字节流长度
看下后台打印
但是这样上传只适合小文件,因为上传的文件会占用用户内存,太大的话会把内存撑爆
2.多文件上传
#多文件上传
@app.post("/multiFiles/")
async def create_files(files: List[bytes] = File()):for file in files:print(len(file))return {"file_sizes": [len(file) for file in files]}
点一次Add string item,就会增加一个文件上传按钮
看下后台打印
4、大文件上传
文件比较大时,如果一次性上传,可能会把用户内存撑爆,因此比较常见的处理方式就是分批上传。
上传大文件使用fastapi的UploadFile
1.单文件上传
from fastapi import FastAPI, File, UploadFile# file: UploadFile:适合大文件上传,比较常用@app.post("/uploadFile/")
#直接对应UploadFile类型数据
async def create_upload_file(file: UploadFile):#打印文件名称print('file',file.filename)#将上传的文件保存到服务本地with open(f"{file.filename}", 'wb') as f:#一次读取1024字节,循环读取写入for chunk in iter(lambda: file.file.read(1024), b''):f.write(chunk)return {"filename": file.filename}
后台打印
可以看到上传的文件被保存在服务端本地
单文件上传完整代码:
from fastapi import FastAPI, File, UploadFile
from typing import List
import uvicornapp = FastAPI()# file: UploadFile:适合大文件上传,比较常用@app.post("/uploadFile/")
#直接对应UploadFile类型数据
async def create_upload_file(file: UploadFile):#打印文件名称print('file',file.filename)#将上传的文件保存到服务本地with open(f"{file.filename}", 'wb') as f:#一次读取1024字节,循环读取写入for chunk in iter(lambda: file.file.read(1024), b''):f.write(chunk)return {"filename": file.filename}if __name__ == '__main__':#注意,run的第一个参数 必须是文件名:应用程序名uvicorn.run("文件上传:app", port=8080, reload=True)
2.多文件上传
#上传多个文件
@app.post("/multiUploadFiles/")
async def create_upload_files(files: List[UploadFile]):for file in files:print(file.filename)# 将上传的文件保存到服务本地path = os.path.join('images',f'{file.filename}')with open(path, 'wb') as f:# 一次读取1024字节,循环读取写入for chunk in iter(lambda: file.file.read(1024), b''):f.write(chunk)return {"filenames": [file.filename for file in files]}
看下后台打印,以及上传的文件
查看下载的文件
多文件上传代码:
from fastapi import FastAPI, File, UploadFile
from typing import List
import uvicorn
import osapp = FastAPI()#上传多个文件
@app.post("/multiUploadFiles/")
async def create_upload_files(files: List[UploadFile]):for file in files:print(file.filename)# 将上传的文件保存到服务本地path = os.path.join('images',f'{file.filename}')with open(path, 'wb') as f:# 一次读取1024字节,循环读取写入for chunk in iter(lambda: file.file.read(1024), b''):f.write(chunk)return {"filenames": [file.filename for file in files]}if __name__ == '__main__':#注意,run的第一个参数 必须是文件名:应用程序名uvicorn.run("文件上传:app", port=8080, reload=True)
怎么样小伙伴,使用fastapi实现文件上传是不是很简单,有兴趣抓紧试试吧!