Django REST Framework 学习笔记(五):异常模块 exceptions

  • Title(EN): Django REST Framework Learning Notes (5): the exceptions module
  • Author: dog2

基本信息

异常处理

DRF异常处理

  1. 所有经过drf的APIView视图类产生的异常,都可以提供异常处理方案
  2. drf默认提供了异常处理方案rest_framework.views.exception_handler,但是处理范围有限
  3. drf提供的处理方案两种,处理了返回异常现象,没处理返回None(后续就是服务器抛异常给前台)

通常出于一些原因我们需要自定义异常处理,而非使用DRF的默认异常。

基于DRF自定义异常处理

自定义异常处理的常见应用场景如下:

  1. 解决drf没有处理的异常
  2. 让前台得到合理的异常信息返回、隐藏异常细节而返回通用异常信息
  3. 后台记录异常具体信息、如将异常细节写入日志以供审计等

源码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 异常模块:APIView类的dispatch方法中
response = self.handle_exception(exc) # 点进去

#自定义异常就是提供异常处理函数exception_handler,处理的目的就是让response一定有值
#自定义:自己写exception_handler函数,在自己的配置文件配置EXCEPTION_HANDLER指向自己的处理异常函数
exception_handler = self.get_exception_handler() #点进去 #获取处理异常的方法,也可以自定义异常处理方法,在settings文件中配置

#settings中获取异常处理的方式
return self.settings.EXCEPTION_HANDLER

# APIView类属性settings
settings = api_settings

# api_settings中定义的默认异常处理函数,又指向回了views.exception_handler函数
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'

# 异常处理的结果
# 自定义异常就是提供exception_handler异常处理函数,处理的目的就是让response一定有值
response = exception_handler(exc, context) #最后发现在views中的exception_handler就是处理异常的方法

dispatch中的handle_exception进入,get_exception_handler()获得处理异常方法exception_handler(),在这里也可以自定义异常方法。执行exception_handler()获取异常处理的结果。

用法示例

自定义全局配置

这是视频教程里的做法,它对于所有视图都采用自己定制的异常处理函数。

  1. 在项目的settings.py
1
2
3
4
5
# 修改自己的配置文件setting.py
REST_FRAMEWORK = {
# 全局配置异常模块
'EXCEPTION_HANDLER': 'api.exception.exception_handler', #设置自定义异常文件路径,在api应用下创建exception文件,exception_handler函数
}
  1. 创建自定义异常处理函数,函数内部逻辑一般为
    1. 先将异常处理交给rest_framework.viewsexception_handler去处理
    2. 判断处理的结果(返回值)response,有值代表drf已经处理了,None代表drf处理不了的异常,需要自定义去处理

api应用下创建处理异常文件exception.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 自定义异常处理文件exception,重写exception_handler函数
from rest_framework.views import exception_handler as drf_exception_handler #drf原生处理异常函数取别名 drf_exception_handler
from rest_framework.views import Response
from rest_framework import status
def exception_handler(exc, context): #自定义处理异常函数
# drf的exception_handler做基础处理
response = drf_exception_handler(exc, context)
# 为空,就是drf框架处理不了的异常
if response is None: #处理之后为空,再进行自定义的二次处理
# print(exc) #错误原因 还可以做更详细的原因,通过判断exc信息类型
# print(context) #错误信息
print('%s - %s - %s' % (context['view'], context['request'].method, exc))
return Response({
'detail': 'global customized exception: internal server error' # 全局定制异常:服务器错误
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR, exception=True)
return response #处理之后有值,就直接返回结果

自定义局部配置

这是我拓展的方法,它可以针对某个特定的视图类,定制不同于全局定制异常的特有处理方式。

根据前面的源码分析中可以知道,要为视图类单独定制异常处理函数,可以这样做:

  1. 在继承APIView编写视图类时,重写父类的get_exception_handler函数,让异常处理函数指向自定义的异常处理函数。
  2. 在视图类中加入定制的异常处理函数。

示例代码如下:

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
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
# drf原生处理异常函数取别名 drf_exception_handler
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.views import Response
from rest_framework import status

@method_decorator(csrf_exempt, name='dispatch')
class LocalExceptionView(APIView):
def post(self, request, *args, **kwargs):
print(request.query_params)
print(request.data)
print(request.not_exists) # error code
return Response('post ok')

def get_exception_handler(self):
return self.exception_handler

def exception_handler(self, exc, context):
# drf的exception_handler做基础处理
response = drf_exception_handler(exc, context)
# 为空,就是drf框架处理不了的异常
if response is None: #处理之后为空,再进行自定义的二次处理
# print(exc) #错误原因 还可以做更详细的原因,通过判断exc信息类型
# print(context) #错误信息
print('%s - %s - %s' % (context['view'], context['request'].method, exc))
return Response({
'detail': 'local customized exception: internal server error' # 局部定制异常:服务器错误
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR, exception=True)
return response #处理之后有值,就直接返回结果

测试demo

测试代码在这里:no5_drf_exception

其中包含:

  • django项目
    • 全局定制异常
    • 局部定制异常
  • postman测试数据包
    • 全局定制异常测试
    • 局部定制异常测试