响应与错误处理
这是什么:响应控制 = 决定接口「回什么、回什么码」:正常时用合适的状态码(201 创建成功),出错时用 HTTPException 干净利落地返回 404/400,还能用 response_model 把不该给客户端的字段滤掉。
为什么用:接口不是「能返回就行」——客户端要根据状态码分支处理(AJAX 教程里 res.ok 判断的就是它);查无此物回 404、参数非法回 400,是专业 API 的基本修养。
如何用:装饰器里声明 status_code,函数里 raise HTTPException,装饰器里挂 response_model,三件套各管一摊。
返回值与状态码
FastAPI 里你只管 return,dict / Pydantic 对象都会自动序列化成 JSON:
@app.post("/todos", status_code=201)
def create_todo(todo: Todo):
return {"id": 1, **todo.model_dump()}
status_code=201 表示「创建成功」(还记得 AJAX 教程里 POST 常见 201 吗)。常用码速查:
| 状态码 | 含义 | 什么时候用 |
|---|---|---|
| 200 | OK | 默认,GET / PUT 成功 |
| 201 | Created | POST 创建资源成功 |
| 404 | Not Found | 查的资源不存在 |
| 400 | Bad Request | 业务规则不允许(如重复添加) |
HTTPException:出错的正确姿势
查不到数据时,不要返回 {"error": "没找到"} 加 200——要用异常把状态码一起抛出去:
from fastapi import FastAPI, HTTPException
app = FastAPI()
todos = {"1": "写周报", "2": "交房租"}
@app.get("/todos/{todo_id}")
def read_todo(todo_id: str):
if todo_id not in todos:
raise HTTPException(status_code=404, detail="待办不存在")
return {"id": todo_id, "title": todos[todo_id]}
GET /todos/1 → 200 {"id": "1", "title": "写周报"}
GET /todos/99 → 404 {"detail": "待办不存在"}
raise 出去后 FastAPI 自动把它变成对应的 HTTP 错误响应,detail 会出现在响应 JSON 里。函数体里只写「正常逻辑」,出错就 raise,不用层层 return 错误标记——代码干净,客户端也能靠状态码分支。
response_model:输出过滤阀
模型里有些字段只想内部用(密码、创建成本),不该回给客户端。加一个 response_model 声明「对外只给这些」:
from pydantic import BaseModel
class UserIn(BaseModel):
username: str
password: str # 客户端提交的形状
class UserOut(BaseModel):
username: str # 对外只暴露用户名
@app.post("/users", response_model=UserOut)
def create_user(user: UserIn):
return user # 存的是完整对象,回出去自动只剩 username
你会看到:POST 提交了 password,响应里却没有它——FastAPI 按 UserOut 把多余字段滤掉了。入参模型和出参模型分开,是防敏感信息泄露的标准做法。
❓ 常见问题
Q1:404 和 422 到底谁管什么?
422 是「请求格式不对」(自动校验拦下:类型错、缺字段),发生在进入函数体之前;404 是「请求格式对,但东西不存在」(你在函数体里 raise 的)。看到 422 改参数,看到 404 查数据。
Q2:能不能同时返回自定义结构和错误码?
能,但别这么设计。成功用 200/201 + 数据,失败用 4xx + detail;用 200 包一层 {"success": false} 是老式做法,会让客户端无法用 res.ok 统一判断(前端那章专门强调过)。
Q3:detail 只能给字符串吗?
可以给任意 JSON 结构,比如 detail={"reason": "库存不足", "stock": 0},客户端能拿到结构化错误信息。简单场景字符串就够。
记忆口诀:创建用 201,查无抛 404;response_model 站门口,敏感字段别外流。
📝 本章小结
return自动转 JSON;status_code=201标记创建成功- 出错
raise HTTPException(status_code, detail),状态码与信息一次给全 - 422 = 格式错(自动校验),404/400 = 业务错(你主动抛),职责分明
response_model过滤输出字段,入参出参模型分离防泄密
动手改
- 给 POST
/todos加status_code=201,在/docs里看响应码变化 - 给
read_todo补一个「id 非数字」的处理思路:把路径参数改成int(回顾第 3 章),422 自动接管 - 给
UserIn加email字段并让UserOut也含它,验证过滤只看出参模型
上一章:请求体与 Pydantic · 下一章:数据库入门 🐸