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