FastAPI 进阶实战:请求体、文件上传、响应模型与数据校验 FastAPI 进阶实战请求体、文件上传、响应模型与数据校验在前一篇 FastAPI 入门中我们掌握了路由、参数、异常和异步基础。本文将深入进阶功能Pydantic 模型嵌套与自定义校验、文件上传与保存、表单参数、响应模型过滤敏感字段以及如何组织代码。通过完整的用户注册接口实战带你写出生产级 API。目录代码组织最佳实践请求体Pydantic 模型详解2.1 定义请求体模型2.2 自动解析 JSON响应模型过滤与格式化文件上传与保存4.1 单文件上传4.2 多文件上传4.3 文件保存技巧MD5 重命名、扩展名提取表单参数Form 处理混合表单与文件Pydantic 进阶Field 参数与自定义校验7.1 Field 常用参数7.2 自定义校验器field_validator7.3 模型嵌套与 List 类型综合实战用户注册接口含头像上传总结与扩展1. 代码组织最佳实践当项目变大时合理的代码组织至关重要。推荐结构myproject/ ├── main.py # 应用入口 ├── models/ # Pydantic 模型 │ ├── user.py │ └── ... ├── routers/ # 路由模块APIRouter │ ├── user.py │ └── ... ├── utils/ # 工具函数 │ ├── file_utils.py │ └── ... ├── uploads/ # 上传文件存储目录 └── requirements.txt快速实践使用if __name__ __main__控制启动。抽取公共函数如保存文件、查找用户到utils。使用response_model保证输出格式一致。用 Pydantic 模型定义请求体和响应体避免字典散落。2. 请求体Pydantic 模型详解2.1 定义请求体模型继承BaseModel声明字段及类型。frompydanticimportBaseModelclassUserCreate(BaseModel):username:stremail:strage:intFastAPI 自动解析 JSON 请求体为模型实例。2.2 自动解析 JSON 请求体fromfastapiimportFastAPI appFastAPI()app.post(/users)defcreate_user(user:UserCreate):# FastAPI 自动从 JSON 解析return{username:user.username,age:user.age}客户端发送{username: alice, email: ab.com, age: 25}自动校验类型并转换。3. 响应模型过滤与格式化使用response_model参数可以自动过滤未在模型中定义的字段如密码。保证输出格式一致。支持嵌套模型。classUserResponse(BaseModel):username:stremail:str# 注意不包含 password 字段app.post(/users,response_modelUserResponse)defcreate_user(user:UserCreate):# 假设实际存储的 user 有 password 字段但响应中不会返回db_user{username:user.username,email:user.email,password:secret}returndb_user# 自动过滤 password也可以直接返回模型实例return UserResponse(username..., email...)或字典FastAPI 会自动适配。4. 文件上传与保存4.1 单文件上传使用UploadFile类型配合File()。fromfastapiimportFile,UploadFileapp.post(/upload)asyncdefupload_file(file:UploadFileFile(...)):contentawaitfile.read()return{filename:file.filename,size:len(content)}4.2 多文件上传fromtypingimportListapp.post(/upload-multiple)asyncdefupload_multiple(files:List[UploadFile]File(...)):forfileinfiles:contentawaitfile.read()# 处理每个文件return{count:len(files)}4.3 文件保存技巧MD5 重命名、扩展名提取实际生产中需避免文件名冲突通常使用 MD5 或 UUID 重命名。importosimporthashlibimportaiofilesasyncdefsave_file(file:UploadFile,upload_dir:struploads)-str:# 1. 读取内容contentawaitfile.read()# 2. 计算 MD5md5hashlib.md5(content).hexdigest()# 3. 获取扩展名extos.path.splitext(file.filename)[1]# 如 .png# 4. 生成唯一文件名safe_filenamef{md5}{ext}file_pathos.path.join(upload_dir,safe_filename)# 5. 创建目录如果不存在os.makedirs(upload_dir,exist_okTrue)# 6. 写入文件asyncwithaiofiles.open(file_path,wb)asf:awaitf.write(content)returnfile_path然后在路由中使用app.post(/upload-avatar)asyncdefupload_avatar(avatar:UploadFileFile(...)):pathawaitsave_file(avatar)return{path:path}5. 表单参数Form 处理当客户端使用application/x-www-form-urlencoded或multipart/form-data提交表单数据非 JSON时使用Form()。fromfastapiimportFormapp.post(/login)deflogin(username:strForm(...),password:strForm(...)):# 验证逻辑return{username:username}支持默认值和校验username:strForm(...,min_length4,max_length20)6. 混合表单与文件文本字段用Form()文件字段用File()可同时使用。app.post(/register)asyncdefregister(username:strForm(...),age:intForm(...,ge18),avatar:UploadFileFile(...)):avatar_pathawaitsave_file(avatar)return{username:username,age:age,avatar:avatar_path}⚠️ 注意此时客户端必须使用multipart/form-data编码不能使用 JSON。7. Pydantic 进阶Field 参数与自定义校验7.1 Field 常用参数在模型字段中使用Field添加校验和描述。参数说明default默认值若为必填则使用...gt大于greater thange大于等于lt小于le小于等于min_length字符串最小长度max_length字符串最大长度pattern正则表达式description文档描述frompydanticimportBaseModel,FieldclassUserCreate(BaseModel):username:strField(...,min_length3,max_length20,description用户名)age:intField(...,ge0,le150)email:strField(...,patternr^\S\S\.\S$)7.2 自定义校验器field_validator使用装饰器对单个字段进行自定义校验抛出ValueError即可。frompydanticimportfield_validatorclassUserCreate(BaseModel):username:strpassword:strconfirm_password:strfield_validator(confirm_password)classmethoddefpasswords_match(cls,v,info):ifv!info.data.get(password):raiseValueError(两次密码不一致)returnv在 Pydantic v2 中推荐使用field_validator之前为validator。可通过values参数访问其他字段。7.3 模型嵌套与 List 类型模型字段可以是另一个模型对象或列表。classAddress(BaseModel):city:strstreet:strclassUserWithAddress(BaseModel):username:straddresses:List[Address]# 嵌套模型列表# 使用示例data{username:alice,addresses:[{city:Beijing,street:Xizhimen},{city:Shanghai,street:Nanjing Road}]}userUserWithAddress(**data)8. 综合实战用户注册接口含头像上传下面实现一个完整的用户注册接口包含请求体 JSON用户名、邮箱、密码表单文件头像响应模型不返回密码文件保存MD5 重命名自定义校验密码强度、邮箱格式importosimporthashlibimportrefromfastapiimportFastAPI,File,UploadFile,Form,HTTPExceptionfrompydanticimportBaseModel,Field,field_validatorfromtypingimportOptionalimportaiofiles appFastAPI(title用户注册API)# ---------- 请求体模型 ----------classUserRegisterRequest(BaseModel):username:strField(...,min_length3,max_length20)email:strField(...,patternr^\S\S\.\S$)password:strField(...,min_length6)field_validator(password)classmethoddefstrong_password(cls,v):ifnotre.search(r\d,v):raiseValueError(密码必须包含数字)ifnotre.search(r[A-Za-z],v):raiseValueError(密码必须包含字母)returnv# ---------- 响应模型不返回密码 ----------classUserResponse(BaseModel):username:stremail:stravatar_url:Optional[str]None# ---------- 文件保存工具 ----------asyncdefsave_avatar(file:UploadFile)-str:contentawaitfile.read()# 仅允许图片ifnotfile.content_type.startswith(image/):raiseHTTPException(400,只允许上传图片)md5hashlib.md5(content).hexdigest()extos.path.splitext(file.filename)[1]safe_namef{md5}{ext}upload_diruploads/avatarsos.makedirs(upload_dir,exist_okTrue)file_pathos.path.join(upload_dir,safe_name)asyncwithaiofiles.open(file_path,wb)asf:awaitf.write(content)returnf/{file_path}# 模拟访问URL# ---------- 注册接口 ----------app.post(/register,response_modelUserResponse)asyncdefregister(user_data:UserRegisterRequest,# JSON 请求体avatar:UploadFileFile(...)# 文件字段):# 模拟保存用户信息到数据库avatar_urlawaitsave_avatar(avatar)# 返回响应不包含密码returnUserResponse(usernameuser_data.username,emailuser_data.email,avatar_urlavatar_url)# ---------- 启动 ----------if__name____main__:importuvicorn uvicorn.run(app,host0.0.0.0,port8000)测试方式使用 Postman 或 FastAPI 自动文档/docs。选择multipart/form-data方式同时提供 JSON 字段注意需要将 JSON 转为键值对复杂结构建议分开为表单字段或者将 JSON 序列化后放在一个字段中。更常见做法将用户信息也拆成表单字段但本例演示了混合 JSON文件的高级用法实际应用中需注意客户端需将 JSON 对象序列化后以字符串形式发送后端再用json.loads解析。更推荐全部使用表单字段或全部使用 JSON文件单独上传。为简化上述示例假定客户端能将 JSON 数据以表单字段方式发送不推荐复杂 JSON。更好的设计用两个接口分开先上传文件获得 URL再提交 JSON。实际生产建议将文件上传和用户信息创建分为两步或使用multipart/form-data的文本字段传递用户信息的 JSON 字符串。9. 总结与扩展本文涵盖了 FastAPI 进阶开发的核心内容功能关键点代码组织分层结构、公共函数、启动控制请求体Pydantic 模型、自动 JSON 解析响应模型response_model过滤敏感字段文件上传UploadFileFile()、异步读写、MD5 重命名表单参数Form()处理 urlencoded/form-data混合表单与文件Form()与File()同时使用Pydantic 校验Field参数、field_validator、模型嵌套下一步学习学习 ORM 框架如 SQLAlchemy与数据库集成掌握增删改查、事务管理。理解 RESTful API 设计规范合理使用 HTTP 方法、状态码、资源路径。掌握子路由拆分模块避免单文件臃肿。使用依赖注入Depends抽离公共逻辑分页、认证、数据库会话。学习中间件统一处理请求与响应日志、耗时、跨域。配置 CORS 解决前后端分离的跨域问题并学会挂载静态资源。FastAPI 的高效与优雅使其成为 Python Web 开发的首选。希望你通过本文能写出更健壮、可维护的 API。