🐸 青蛙编程网
首页 / Python / FastAPI / 5. 响应与错误处理

响应与错误处理

这是什么:响应控制 = 决定接口「回什么、回什么码」:正常时用合适的状态码(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 过滤输出字段,入参出参模型分离防泄密

动手改

  1. 给 POST /todos 加 status_code=201,在 /docs 里看响应码变化
  2. 给 read_todo 补一个「id 非数字」的处理思路:把路径参数改成 int(回顾第 3 章),422 自动接管
  3. 给 UserIn 加 email 字段并让 UserOut 也含它,验证过滤只看出参模型

上一章:请求体与 Pydantic · 下一章:数据库入门 🐸

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。