FastAPI 基础篇:请求与响应系统详解
在Web服务中,HTTP请求(Request)和HTTP响应(Response)是客户端和服务端交互的核心机制。
客户端通过发送请求来获取资源或执行操作,服务端则根据请求内容返回相应的响应数据。
FastAPI 提供了简洁高效的Request和Response对象,帮助开发者方便地处理传入的请求数据和构建返回的响应内容。例如:
- 通过
Request对象可以轻松获取路径参数、查询参数、请求体、请求头、Cookie 等信息。 - 通过
Response对象可以自定义状态码、响应头、响应体,并支持 JSON、HTML、纯文本、文件流等多种格式输出。
一、请求(Request)
1. 查询参数(Query Parameters)
查询参数(Query Parameters) 是指 URL 中?之后的部分。多个参数之间用&分隔
基础用法
新建一个user/router.py
fromfastapiimportAPIRouter router=APIRouter(prefix="/user",tags=["用户"])@router.get("")asyncdefget_users(user_name:str,# 必填参数,不填会报错age:int=0,# 有默认值的查询参数,不传给默认值address:str|None=None,# 可选查询参数,不传也行):user={"user_name":user_name,"age":age,"address":address}returnuser在main.py中注册
fromuser.routerimportrouterasuser_router# 创建fastapi实例app=FastAPI()# 注册路由app.include_router(user_router)请求测试
必填参数不传会返回错误
参数校验
FastAPI 通过Query和Annotated为查询参数提供声明式校验能力。开发者可以直接在参数定义处配置长度限制、数值范围、正则匹配等规则,并自动生成接口文档,无需编写额外的校验代码。
在user/router.py新增一个接口
@router.get("/page")asyncdefpage_users(# 必填参数user_name:Annotated[str,Query(min_length=2,max_length=20,description="用户名,长度2~20个字符")],# 有默认值age:Annotated[int,Query(ge=0,le=150,description="年龄,范围0~150")]=18,# 可选参数address:Annotated[str|None,Query(max_length=100,description="地址")]=None,# 正则校验phone:Annotated[str|None,Query(pattern=r"^1[3-9]\d{9}$",description="手机号")]=None,# 分页参数page:Annotated[int,Query(ge=1,description="页码")]=1,size:Annotated[int,Query(ge=1,le=100,description="每页条数")]=10,):return{"user_name":user_name,"age":age,"address":address,"phone":phone,"page":page,"size":size}在文档上也会体现对应的规则
Query 常用参数
| 参数 | 作用 | 示例 |
|---|---|---|
| description | 描述信息 | description="年龄" |
| min_length | 最小长度 | min_length=2 |
| max_length | 最大长度 | max_length=20 |
| pattern | 正则表达式 | pattern=r"^\d+$" |
| ge | 大于等于 | ge=0 |
| gt | 大于 | gt=0 |
| le | 小于等于 | le=100 |
| lt | 小于 | lt=100 |
| alias | 参数别名 | alias="userName" |
| deprecated | 标记废弃 | deprecated=True |
Pydantic 模型
对于参数较多的查询接口,推荐使用 Pydantic 模型封装 Query 参数。这样既能享受 Pydantic 的数据校验能力,又能让接口定义更加简洁清晰,特别适用于分页、筛选等复杂查询场景。
新建一个user/schemas.py
frompydanticimportBaseModel,FieldclassUserQuery(BaseModel):user_name:str|None=Field(default=None,max_length=20)age:int|None=Field(default=None,ge=0,le=150)address:str|None=Nonepage:int=Field(default=1,ge=1)size:int=Field(default=10,ge=1,le=100)在user/router.py中新增一个测试接口
@router.get("/page2")asyncdefpage_users2(# 使用Annotated是为了告诉FastAPI:# UserQuery 的数据来源于 Query 参数而不是 Request Bodyquery:Annotated[UserQuery,Query()]):# 使用model_dump,也会对返回前的对象进行校验。returnquery.model_dump()2. 路径参数
路径参数是 URL 路径中的动态部分,通常用于标识某个具体资源。FastAPI 会根据函数参数名称自动提取路径中的值,并进行类型转换和校验。
基础用法
@router.get("/detail/{user_id}")asyncdefget_user_detail(user_id:int):# 路径参数会根据类型注解自动转换return{"user_id":user_id}上述接口中{user_id}就是路径参数,路径参数可以有多个:
@router.get("/users/{user_id}/orders/{order_id}")asyncdefget_user_order(user_id:int,order_id:int):return{"user_id":user_id,"order_id":order_id}请求测试
需要的int参数,如果传字符串就会报错
使用 Path 增加校验规则
和 Query 参数类似,FastAPI 提供了Path用于声明路径参数的校验规则和元数据。
@router.get("/detail/{user_id}")asyncdefget_user_detail(user_id:Annotated[int,Path(ge=1,description="用户ID")]):return{"user_id":user_id}一个注意点
# 这是动态路由@router.get("/users/{user_name}")asyncdefget_user(user_name:str):return{"user_name":user_name}# 这是静态路由@router.get("/users/hello")asyncdefhello():return{"msg":"hello"}当我访问 GET /users/hello时会执行哪个函数。取决于那个函数写在前面。像上述的写法就会导致永远只会执行到动态路由,而静态路由不会执行。
所以官方推荐的是:静态路由写在前面,动态路由写在后面:
# 这是静态路由@router.get("/users/hello")asyncdefhello():return{"msg":"hello"}# 这是动态路由@router.get("/users/{user_name}")asyncdefget_user(user_name:str):return{"user_name":user_name}3. 请求体参数(Request Body)
前面介绍的 Query 参数和 Path 参数都来自 URL, 而对于创建、修改等操作,需要传递大量结构化数据,此时通常使用请求体(Request Body)。 在 FastAPI 中,请求体通常使用Pydantic 模型定义,FastAPI 会自动完成请求数据解析 ,类型转换,参数校验 ,Swagger 文档生成 。
基础用法
在user/schemes.py中定义UserCreateRequest
classUserCreateRequest(BaseModel):# 定义请求体字段包含参数校验user_name:str=Field(min_length=2,max_length=20)age:int=Field(ge=0,le=150)在user/router.py新增接口
@router.post("")asyncdefcreate_user(request:UserCreateRequest):returnrequest.model_dump()在文档测试
请求体与查询参数混用
@router.post("")asyncdefcreate_user(request:UserCreateRequest,notify:bool=False):return{"user":request.model_dump(),"notify":notify}请求:
POST /user?notify=true请求体:
{ "user_name": "Tom", "age": 18 }嵌套对象
请求体最大的优势就是支持复杂结构。
frompydanticimportBaseModelclassAddress(BaseModel):city:strstreet:strclassUserCreateRequest(BaseModel):user_name:strage:int# 嵌套对象address:Address请求体样例如下:
{"user_name":"Tom","age":18,"address":{"city":"Shanghai","street":"Pudong"}}4. 表单参数
Form 参数用于接收表单提交的数据,常见于登录、注册等传统 HTML 表单场景,数据格式为 application/x-www-form-urlencoded 或 multipart/form-data。
基础用法
在auth/router.py中新增一个登陆接口
router=APIRouter(prefix="/auth",tags=["认证"])@router.post("/login")asyncdeflogin(username:str=Form(),password:str=Form()):return{"username":username}在文档中测试可以看到
POST/auth/login Content-Type:application/x-www-form-urlencoded请求体为:
username=admin&password=123456Form + File 上传(UploadFile)
新增一个表单参数和文件混合的接口
@router.post("/profile")asyncdefupdate_profile(# ... 是 Python 内置的 Ellipsis 对象,FastAPI 只是借用它来表示“必填参数”,# FastAPI 全部组件都支持:Query(...),Path(...),Form(...)username:str=Form(...),age:int=Form(...),avatar:UploadFile=File(...)):return{"username":username,"age":age,"file":avatar.filename}当表单中包含文件时, Content-Type就是:multipart/form-data
5. 请求头
HTTP 请求头用于携带元信息,比如:
- token
- user-agent
- language
- content-type
基础用法
我们继续在auth/router.py新增一个接口
@router.get("/headers")asyncdefget_headers(token:str|None=Header(default=None)):return{"token":token}测试一下,能成功获取到token
一个注意点
Header 默认会做自动转换命名。
因为 Python 变量不能有-,FastAPI会把user_agent转成User-Agent:
@router.get("/headers")asyncdefget_headers(token:str|None=Header(default=None),#可以通过Header(convert_underscores=False)关闭自动转换,#一般不这样做user_agent:str=Header()):return{"token":token,"user-agent":user_agent}6. cookie
Cookie 是浏览器存储在客户端的小数据, 浏览器会自动保存 ,自动随请求发送。
基础用法
fromfastapiimportCookie@router.get("/cookie")asyncdefget_cookie(session_id:str|None=Cookie(default=None)):return{"session_id":session_id}在文档上测试cookie发现传了没用。这里切换成专业的api工具测试(apifox)。
二、响应(Response)
FastAPI 默认会把你返回的 Python 数据(如字典、列表、Pydantic 模型)自动转成 JSON 并返回,不需要你手动做序列化。
如果要返回 HTML、文件或流数据,可以使用 FastAPI 提供的专门响应类型(如HTMLResponse、FileResponse、StreamingResponse)。
1. JSONResponse
这是FastAPI默认的响应类型:
content-type:application/json基本用法
在src/resp/router.py中新增一个接口
fromfastapiimportAPIRouter,Queryfromstarlette.responsesimportJSONResponse router=APIRouter(prefix="/resp",tags=["响应测试"])@router.get("/json")defjson_demo():returnJSONResponse(content={"msg":"ok","code":200})进行测试
定制Response
我们也可以对Response对象进行设置来返回具体的内容和响应头,响应码等信息。
@router.get("/json")defjson_demo():resp=JSONResponse(content={"msg":"ok","code":200},status_code=200,headers={"Content-Type":"application/json"})resp.set_cookie(key="session_id",value="abc123")returnrespresponse_class
在 FastAPI 中,可以在装饰器上通过response_class和status_code来预先定义响应类型和状态码,这样在函数内部只需要返回“原始数据”,不需要手动构造 Response,使代码更简洁。
@router.get("/json",status_code=200,response_class=JSONResponse)defjson_demo():return{"msg":"ok","code":200}2. HTMLResponse
用于返回html网页。响应类型为:
content-type:text/html;基础用法
@router.get("/html",response_class=HTMLResponse)defhtml_demo():return"<h1 style='color:red'>Hello FastAPI</h1>"进行测试
3. PlainTextResponse
用于返回纯文本。响应类型为:
content-type:text/plain基础用法
@router.get("/text")deftext_demo():returnPlainTextResponse("hello world")进行测试
4. FileResponse
主要用于文件下载,浏览器访问会直接下载文件。响应类型为:
content-disposition:attachment;filename="my-image.png"content-type:image/png基础用法
@router.get("/download")defdownload():returnFileResponse(# 服务器的文件path="D:/temp/1.png",# 下载的文件名filename="my-image.png")5. StreamingResponse
流式响应:一边生成一边返回 ,避免一次性返回大量的数据
示例1
下载大文件
deffile_stream():""" 大文件分段下载不会爆内存 """withopen("bigfile.zip","rb")asf:whilechunk:=f.read(1024*1024):yieldchunk@router.get("/stream-file")defstream_file():returnStreamingResponse(file_stream())示例2
模拟ai流式响应
defai_stream():forwordin["Hello","FastAPI","Streaming"]:yield(word+" ").encode("utf-8")time.sleep(0.5)@router.get("/ai")defai():returnStreamingResponse(ai_stream())6. RedirectResponse
RedirectResponse 用于让接口返回一个重定向指令,使客户端自动跳转到另一个 URL。
基础用法
@router.get("/login")deflogin():# 登录成功后跳转到首页returnRedirectResponse(url="/home")7. response_model
response_model用来定义接口“返回数据的结构”,FastAPI 会自动做过滤 + 校验 + 文档生成。
基础用法
定义接口和模型对象
classUser(BaseModel):name:strage:int@router.get("/user",response_model=User)# 最终返回的数据必须“符合 User 模型结构”defget_user():return{"name":"Tom","age":18,"password":"secret"# 会被自动过滤掉}测试一下
对于 FastAPI 中请求与响应的基础内容,我们就先介绍到这里。关于更多 深入 FastAPI 的内容,我们下期再见👋。
