Fastapi中ResponseModel的设计

  • 前言

    在我学习ruoyi-fastapi-backend开源项目的时候让我思考了一下关于responsemodel的设计

    在这个项目中swagger文档中的示例模型是这样的

1
2
3
4
5
6
7
8
9
10
11
#model
class Token(BaseModel):
"""
token响应模型
"""
access_token: str= Field(description='token信息')

#swagger示例
{
"access_token": "string"
}

​ 并没有显示code或者其他的 而是单单显示该模型

这令我想转牛角尖 为什么不把code也给加上去呢

首先来看看在ruoyi-fastapi-backend项目中response使用的是一个工具类来动态组装response

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class ResponseUtil:
@classmethod
def success(
cls,
msg: str = '操作成功',
data: Optional[Any] = None,
rows: Optional[Any] = None,
dict_content: Optional[Dict] = None,
model_content: Optional[BaseModel] = None
)-> Response:
"""
成功响应方法
:param msg: 可选,自定义成功响应信息
:param data: 可选,成功响应结果中属性为data的值
:param rows: 可选,成功响应结果中属性为rows的值
:param dict_content: 可选,dict类型,成功响应结果中自定义属性的值
:param model_content: 可选,BaseModel类型,成功响应结果中自定义属性的值
:return:
"""

result = {'code': HttpStatusConstant.SUCCESS,'msg': msg}
if data is not None:
result['data'] = data
if rows is not None:
result['rows'] = rows
if dict_content is not None:
result.update(dict_content)
if model_content is not None:
result.update(model_content.model_dump(by_alias=True))

result.update({'success': True, 'time': datetime.now()})

return JSONResponse(status_code=status.HTTP_200_OK, content=jsonable_encoder(result))

#返回示例
@loginController.post(
'/login3',
response_model=Token1,
)
async def login3(xxx):
responsemodel = Token1(access_token=jwt_token)
return ResponseUtil.success(data=responsemodel)

response_model=Token1这样就会导致swagger中没有code、msg对应的pydantic模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
在示例文档中
{
"access_token": "string"
}
#在实际请求时
{
"code": 200,
"msg": "操作成功",
"data": {
"access_token": "xxx"
},
"success": true,
"time": "2025-02-05T01:54:30.779036"
}

所以我就自己设计了一下自己的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
T = TypeVar('T')


class BaseResponse(BaseModel,Generic[T]):
"""
响应模型基类 自定义响应模型应继承此类 eg:{code,msg,success,time,**T}
"""
code: int = Field(default=None, description='响应状态码')
msg: str = Field(default=None, description='响应信息')
success: bool = Field(default=None, description='是否成功')
time: datetime = Field(default=None, description='响应时间')
data: T = Field(default=None,description='数据信息')

@classmethod
def ok(
cls,
data: Optional[Any] = None,
rows: Optional[Any] = None,
model_content: Optional[BaseModel] = None
):
result = {}
if model_content is not None:
result.update(model_content.model_dump(by_alias=True))
if rows is not None:
result['rows'] = rows
if data is not None:
result['data'] = data
result['code'] = HttpStatusConstant.SUCCESS
result['msg'] = '操作成功'
result['success'] = True
result['time'] = datetime.now()
return result

用这种方式既可以在swagger上面显示code的字段 也可以很方便的返回response

嵌套的方式response_model=BaseResponse[Token1] BaseResponse.ok(data=responsemodel)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Token1(BaseModel):
"""
token响应模型
"""
access_token: str= Field(description='token信息')

@loginController.post(
'/login2',
response_model=BaseResponse[Token1],
)
async def login2():
responsemodel = Token1(access_token=jwt_token)
return BaseResponse.ok(data=responsemodel)
#swagger
{
"code": 0,
"msg": "string",
"success": true,
"time": "2025-02-04T19:33:33.929Z",
"data": {
"access_token": "string"
}
}

Model作为请求体的方式

Model继承BaseResponse response_model=Token BaseResponse.ok(model_content=responsemodel)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Token(BaseResponse):
"""
token响应模型
"""
access_token: str= Field(description='token信息')

@loginController.post(
'/login',
response_model=Token
)
async def login():
responsemodel = Token(access_token=jwt_token)
return BaseResponse.ok(model_content=responsemodel)
#swagger
{
"code": 0,
"msg": "string",
"success": true,
"time": "2025-02-04T19:33:33.926Z",
"data": 1,
"access_token": "string"
}


Fastapi中ResponseModel的设计
https://wantoper.github.io/2025/02/05/old/fastapi-responsemodel/
作者
Wantoper
发布于
2025年2月5日
许可协议