FastAPI教程-响应类型-返回JSON

介绍

FastAPI 默认就返回 JSON,内部基于 JSONResponse,自动做序列化、设置响应头 Content-Type: application/json。下面分常用写法、自定义响应、序列化规则、异常返回、底层原理。

1. 直接返回 dict

直接返回字典,FastAPI 自动转 JSON。

例子

显示一条记录的详细信息,如:查看某个学生的信息

from fastapi import FastAPI

app = FastAPI()

@app.get("/student")
def get_student():
    # 直接返回python字典,框架自动转为json
    return {
        "code": 200,
        "msg": "success",
        "data": {
            "name": "张三",
            "age": 20
        }
    }

响应结果:

{
  "code": 200,
  "msg": "success",
  "data": {
    "name": "张三",
    "age": 20
  }
}

2. 返回模型(推荐但不常用)

这种用法,官方推荐,但开发中,习惯返回 dict 类型,而 不是返回 模型对象

例子1

直接返回模型对象,功能与返回 dict 类似,用于显示一条记录的详细信息,如:显示用户的信息

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class UserResponse(BaseModel):
    id: int
    name: str
    password: str


@app.get("/user")
def get_user():
    user = UserResponse(id=1, name="李四",password="123456")
    return user   # 返回模型对象,自动序列化为json

响应结果:

{
  "id": 1,
  "name": "李四",
  "password": "123456"
}
缺点

将密码也返回给前端了,这是不合理的,会泄露密码

解决

移除模型的 password 字段

例子2:返回模型对象,过滤敏感字段

定义模型,不包含 password:

class UserResponse(BaseModel):
    id: int
    name: str

返回模型对象,既使在对象里 加上 password,也不会返回:

@app.get("/user")
def get_user():
    # 创建对象时传入 password
    user = UserResponse(id=1, name="李四",password="123456")
    return user   # 返回模型对象,自动序列化为json

执行结果,不包含 password:

{
  "id": 1,
  "name": "李四"
}
缺点

一般开发中,习惯返回 dict 类型,而 不是返回 模型对象

解决

在装饰器中设置 response_model

3. 设置 response_model,返回dict(推荐且常用)

在装饰器中设置 response_model,返回 dict 类型

这种用法,即符合官方推荐,又符合开发习惯

好处:

  • 校验 JSON 返回数据,有缺失报错
  • 过滤敏感字段

例子1

校验 JSON 返回数据,有缺失报错

定义模型
class UserResponse(BaseModel):
    id: int
    name: str
    password: str
实现视图函数
  • 装饰器需要 指定响应模型 response_model
  • 视图函数返回 dict
@app.get("/user",response_model=UserResponse)
def get_user():
    return {
        "id":1,
        "name":"李四",
        "password":"123456"
    }

执行结果:

{
  "id": 1,
  "name": "李四",
  "password":"123456"
}

例子2:有缺失报错

返回的数据缺少 password,与 UserResponse 模型字段不匹配,直接报错

@app.get("/user",response_model=UserResponse)
def get_user():
    return {
        "id":1,
        "name":"李四"
    }

执行报错

例子3:过滤敏感字段

很多场景:数据库模型字段很多,不想全部返回给前端,用 response_model 做输出过滤。

注意:返回 dict 中包含 password,通过设置 response_model,会过滤掉 password

定义模型

不包含 password:

class UserResponse(BaseModel):
    id: int
    name: str
实现视图函数
  • 装饰器需要 指定响应模型 response_model
  • 视图函数返回 dict,既使 加上 password,也不会返回
@app.get("/user",response_model=UserResponse)
def get_user():
    return {
        "id":1,
        "name":"李四",
        "password":"123456"
    }

执行结果,不包含 password:

{
  "id": 1,
  "name": "李四"
}

4. 不输出未赋值的字段

还可以用 response_model_exclude_unset=True:不输出未赋值的字段。

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class UserResponse(BaseModel):
    id: int
    # 要设置允许None,否则不设置值会报错
    name: str | None = None
    age: int | None = None


# 返回dict
@app.get("/user",response_model=UserResponse,response_model_exclude_unset=True)
def get_user():
    return {
        "id":1
    }

响应结果:

{
  "id": 1
}

5. 统一返回格式

前后端约定统一格式:{code,msg,data},两种实现方式。

方式1:每个接口手动包一层dict(简单小项目)

@app.get("/info")
def info():
    return {
        "code":200,
        "msg":"ok",
        "data":{
            "name":"李雷",
            "age":18,
        }
    }

方式2:封装工具函数(大项目)

手动构造 JSONResponse,可以自定义 状态码、自定义响应头、直接传入 JSON 字符串

from fastapi.responses import JSONResponse

def resp_ok(data=None,msg="success"):
    return JSONResponse({
        "code":200,
        "msg":msg,
        "data":data
    })

def resp_fail(code=400,msg="fail",data=None):
    return JSONResponse({
        "code":code,
        "msg":msg,
        "data":data
    })

@app.get("/test")
def test():
    return resp_ok(data={"name":"test"})

底层流程

  1. 视图函数 return 对象(dict/pydantic/list)
  2. 调用 jsonable_encoder():把ORM、Pydantic、datetime转成可序列化的python基础类型
  3. 内部构建 JSONResponse 对象
  4. 调用 json.dumps() 序列化为字节流
  5. 设置响应头 Content‑Type: application/json 返回浏览器

注意:如果返回的本身就是 Response(如JSONResponse、HTMLResponse),不会经过jsonable_encoder,直接输出。


原文出处:/show_1GW3rXGXQYTK.html