13-1 自定义异常格式和响应格式-DRF自定义异常格式


目录:

  • 自定义异常的目的
  • DRF默认异常
  • 自定义异常
  • APIView使用自定义异常
  • 报具体错误

一、自定义异常的目的

 其实呐我们之前在写代码的时候,也碰到过一些异常,对吧,这些异常对于我们的PC端其实也还好,但是移动端就不行了,为啥呐?移动端处理异常的时候,如果处理的不及时,会有致命性的bug,如我们最害怕的闪退等一系列的问题。

 目前我们返回的一些异常信息,是这个样子的:

{
    "detail": "Authentication credentials were not provided."
}

  对于这样的结构,我们移动端的同学看到是极其不友好的,所以我们一般给对方的返回这样的数据结构:

{
    "code": 401,
    "message": "Authentication credentials were not provided.",
    "data": []
}

那这个时候我们就需要自己异常来捕获DRF里面的异常信息。

二、DRF默认异常

 其实DRF给我们自定义了默认异常,我们来看看,在APIView中是如何定义的,来走一个:Ctrl + APIView:

class APIView(View):
    ....
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
    ....

好啦,继续: Ctrl + api_settings :

api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)

来,走你,看看默认的是啥: Ctrl + DEFAULTS:

DEFAULTS = {
    ...
    # Exception handling
    'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',   #默认异常
    'NON_FIELD_ERRORS_KEY': 'non_field_errors',
    ....
}

哈哈,找到,那我们只需要重写这个异常,然后覆盖掉,变成自己的不就行啦。 来继续。

三、自定义异常

3.1、目录结构

说明:我们在app下需要手动创建一个文件custom_exception.py 来定义我们的自定义异常。

...
-app06
    -migrations
        ...
    -admin.py
    -apps.py
    -custom_exception.py    #新建自定义异常文件
    ...
....

3.2、自定义异常

说明:自定义异常,就是继承exception_handler,然后重新定义:

from rest_framework.views import exception_handler   #继承默认exception_handler

def custom_exception_handler(exc, context):
    response = exception_handler(exc, context)   #重新定义

    if response is not None:
        response.data.clear()
        response.data['code'] = response.status_code
        response.data['data'] = []

        if response.status_code == 404:
            try:
                response.data['message'] = response.data.pop('detail')
                response.data['message'] = "未找到"
            except KeyError:
                response.data['message'] = "未找到"

        if response.status_code == 400:
            response.data['message'] = '输入错误'

        elif response.status_code == 401:
            response.data['message'] = "未认证"

        elif response.status_code >= 500:
            response.data['message'] = "服务器错误"

        elif response.status_code == 403:
            response.data['message'] = "权限不允许"

        elif response.status_code == 405:
            response.data['message'] = '请求不允许'
        else:
            response.data['message'] = '未知错误'
    return response

3.3、 全局配置

说明:自定义后,需要在 settings.py 中去配置 自定义异常,从而覆盖 默认的,告诉DRF,我不用你的自己默认的,我用我自定义的。

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (...),
     ....
    #自定义异常
    'EXCEPTION_HANDLER': 'app06.custom_exception.custom_exception_handler'
}

3.4、postman测试:

说明:那我们就测试一下,测试之前我们先在我们之前定义的 View中加上认证权限:

...
from rest_framework.permissions import IsAuthenticated   #认证权限

class GameView(ModelViewSet):

    permission_classes = [IsAuthenticated]  #加上认证权限
    queryset = Game.objects.all()
    serializer_class = GameSerializer

效果如图:

四、 APIView使用自定义异常

 我们如果继承的APIView方法,那么我们如何使用自己的自定义异常呐,很简单的,只要在验证的时候传入raise_exception=True即可。

@api_view(['GET', 'POST'])api_view已经帮我们做了验证
def user_list(request):
    if request.method == "GET":
        ....
    elif request.method == "POST":
        ser = UserSerializer(data=request.data, context={'request': request})
        if ser.is_valid(raise_exception=True):   #只需要在在验证的时候 传入raise_exception=True 说明需要使用自定义异常
            ser.save()
            return Response(ser.data, status=status.HTTP_201_CREATED)
        return JSONResponse(ser.errors, status=status.HTTP_401_UNAUTHORIZED)

五、 报具体错误

5.1、报具体报错

说明:怎么个意思呐?就是我们在响应的时候,我们想把自己的具体报错弄出来,所以我们需要对custom_exception_handler代码需要优化,做一个兼容:

from rest_framework.views import exception_handler
from rest_framework.exceptions import ValidationError


def custom_exception_handler(exc, context):
    response = exception_handler(exc, context)
    #对具体报错做了兼容
    if isinstance(exc, ValidationError):
        response.data['code'] = response.status_code
        response.data['data'] = []
        if isinstance(response.data, dict):
            response.data['message'] = list(dict(response.data).values())[0][0]

            for key in dict(response.data).keys():
                if key not in ['code', 'data', 'message']:
                    response.data.pop(key)
        else:
            response.data['message'] = '输入有误'
        return response

    if response is not None:
        ....
    return response

好,这次我们用 postman测试一下:

异常提示,我们在序列化的时候,不仅可以使用 validate_字段名,也可以在具体的字段后面用 error_message:

class UserSerializer(serializers.ModelSerializer):
    phone = serializers.CharField(max_length=11,min_length=11,required=True,error_messages={"required":"手机号码必填"}) #可以在字段这边给出异常提示
    ....

    class Meta:
        ....

    def validate_phone(self, phone):  #单个验证
        if not re.match(r'1[3456789]\d{9}',phone):
            raise serializers.ValidationError("手机号码不合法")
        .....

        return phone

5.2、ModelViewSet使用自定义异常

说明:哈哈,那不禁的有小伙伴要问,我ModelViewSet封装的那么厉害,我如何使用自定义异常呐,其实人家已经建议 使用自定义异常了,不信我们来看看源码:Ctrl + ModelViewSet:

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    """
    A viewset that provides default `create()`, `retrieve()`, `update()`,
    `partial_update()`, `destroy()` and `list()` actions.
    """
    pass

好啦,我们随便找一个我们就进入  CreateModelMixin 进去看看吧:Ctrl + CreateModelMixin:

class CreateModelMixin(object):
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)  #看到了吧raise_exception=True 已经建议你使用自定义异常了
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    ....

哈哈,真香。所以我们在使用 generics.ListCreateApiView 其实都是一样的。都是已经帮你 使用自定义异常了。

5.2、JWT登录使用自定义异常

说明:JWT我们在使用登录的时候,也是需要的,但是我们来看看 JWT登录 其实是没有使用 自定义异常的,不信我们来看看:

Crtl + obtain_jwt_token => ObtainJSONWebToken => JSONWebTokenAPIView

我们找到了,我们定位一下它的视图:

哈哈,我们找到post方法:

class JSONWebTokenAPIView(APIView):
    """
    Base API View that various JWT interactions inherit from.
    """
    permission_classes = ()
    authentication_classes = ()

    def get_serializer_context(self):
        ....

    def get_serializer_class(self):
        ....

    def get_serializer(self, *args, **kwargs):
        ...

    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)

        if serializer.is_valid():   #没有使用我们自定义的,所以只需要 serializer.is_valid(raise_exception=True) 即可,然后保存
            ....
            return response

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

但是我们发现,给出的异常提示还是英文的,那咋办呐,所以我们就需要 去改人家序列化的东西了,那就要碰serializers.py:

 

哈哈,都在这边拉。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM