momo's Blog.

drf自定义response的格式

字数统计: 583阅读时长: 2 min
2021/05/21 Share

前言

我们在前后端分离的时候, 前后端约定的数据格式可能跟DRF自带的数据格式不一样, 那我们如何去自定义格式呢?

我们需要的格式

1
2
3
4
5
6
{
"status": "success",
"code": "", // 200,400,.....etc
"data": [], // data
"message": "" // Success or Error messages
}

实际的格式

1
2
3
4
5
6
7
8
9
10
[
{
"id": 1,
"name": ""
},
{
"id": 2,
"name": ""
}
]

首先想到了便是重写response方法, 当时这样做的话会非常麻烦, 有没有更简单的方法呢?

重写 renderers

让我们自定义

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
from rest_framework.renderers import JSONRenderer

// 继承JSONRenderer
class CustomRenderer(JSONRenderer):
def render(self, data, accepted_media_type=None, renderer_context=None):
status_code = renderer_context['response'].status_code
// 拼接响应数据
response = {
"status": "success",
"code": status_code,
"data": data,
"message": None
}

// 如果状态码不是2开头,则将detail 信息同步到message,并且设置错误状态
if not str(status_code).startswith('2'):
response["status"] = "error"
response["data"] = None
try:
response["message"] = data["detail"]
except KeyError:
response["data"] = data

// 调用父类方法,并返回
return super(CustomRenderer, self).render(response, accepted_media_type, renderer_context)

设置完成以后, 我们需要在settings.py 新增配置

1
2
3
4
5
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': [
'utils.http.CustomRenderer'
]
}

自定义异常处理后的状态码

上面设置完成以后, 我们实际上只能去添加响应数据, 但是如果是报错的信息, 和报错代码呢?一些其他的组件如JWT, 如果你要一个一个改起来也非常的麻烦.

此时我们就需要自定义异常处理的代码

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
from rest_framework.views import exception_handler
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_simplejwt.exceptions import InvalidToken


def custom_exception_handler(exc, context):
# Call REST framework's default exception handler first,
# to get the standard error response.
response = exception_handler(exc, context)
# Now add the HTTP status code to the response.
if response is not None:
if type(exc) == AuthenticationFailed:
response.data['status_code'] = 401
response.data['detail'] = '用户名或者密码错误'
elif type(exc) == InvalidToken:
response.data['status_code'] = 5008
response.data['detail'] = 'Token is invalid or expired'
else:
response.data['status_code'] = response.status_code


# 这里看需求,如果前端要求错误的code也返回200的话,就在这里写死.
# response.status_code = 200
try:
response.status_code = 200
except:
pass
return response

我们还需要添加一下配置

1
2
3
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'utils.http.custom_exception_handler'
}

让我们在修改一下render方法, 换成从data中拿status_code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class CustomRenderer(JSONRenderer):
def render(self, data, accepted_media_type=None, renderer_context=None):
try:
status_code = data.pop('status_code', 200)
message = data.get('detail', None)
except:
status_code = 200
message = None
response = {
"status": "success",
"code": status_code,
"data": data,
"message": message
}

if not str(status_code).startswith('2'):
response["status"] = "error"
response["data"] = None
try:
response["message"] = data["detail"]
except KeyError:
response["data"] = data

return super(CustomRenderer, self).render(response, accepted_media_type, renderer_context)
CATALOG
  1. 1. 前言
  2. 2. 重写 renderers
  3. 3. 自定义异常处理后的状态码