datetime:2020/1/8 16:02
author:nzb

重写异常处理返回

例如:

  • 正常返回
      {
          "detail": "方法 “GET” 不被允许。"
      }
    
  • 重写返回
      {
          "code": 10001,
          "errMsg": "方法 “GET” 不被允许。"
      }
    

源码流程

  • dispath() 分发

      def dispatch(self, request, *args, **kwargs):
          """
          `.dispatch()` is pretty much the same as Django's regular dispatch,
          but with extra hooks for startup, finalize, and exception handling.
          """
          self.args = args
          self.kwargs = kwargs
          request = self.initialize_request(request, *args, **kwargs)
          self.request = request
          self.headers = self.default_response_headers  # deprecate?
    
          try:
              self.initial(request, *args, **kwargs)
    
              # Get the appropriate handler method
              if request.method.lower() in self.http_method_names:
                  handler = getattr(self, request.method.lower(),
                                    self.http_method_not_allowed)
              else:
                  handler = self.http_method_not_allowed
              # 活动到某个视图的某个方法,例如create()
              # 视图函数里的serializer.is_valid(raise_exception=True), 
              # 会去验证序列化里面自带的验证规则和自定义的验证规则(并抛出异常)                            
              response = handler(request, *args, **kwargs)    
    
          except Exception as exc:    
              # 捕获异常
              response = self.handle_exception(exc)
    
          self.response = self.finalize_response(request, response, *args, **kwargs)
          return self.response
    
  • handle_exception()
    # 捕获异常后执行
    def handle_exception(self, exc):
        """
        Handle any exception that occurs, by returning an appropriate response,
        or re-raising the error.
        """
        if isinstance(exc, (exceptions.NotAuthenticated,
                            exceptions.AuthenticationFailed)):
            # WWW-Authenticate header for 401 responses, else coerce to 403
            auth_header = self.get_authenticate_header(self.request)

            if auth_header:
                exc.auth_header = auth_header
            else:
                exc.status_code = status.HTTP_403_FORBIDDEN
        # 获取重写的异常返回函数
        exception_handler = self.get_exception_handler()

        context = self.get_exception_handler_context()
        response = exception_handler(exc, context)

        if response is None:
            self.raise_uncaught_exception(exc)

        response.exception = True
        return response

    # 获取配置文件
    def get_exception_handler(self):
        """
        Returns the exception handler that this view uses.
        """
        return self.settings.EXCEPTION_HANDLER
  • 重写函数custom_exception_handler()

from rest_framework.views import exception_handler
def custom_exception_handler(exc,context):
    """
    框架自带错误码(常见已知的)
                (   (400, "invalid"),
                    (401, "authentication_failed"),
                    (401, "not_authenticated"),
                    (403, "permission_denied"),
                    (404, "not_found"),
                    (405, "method_not_allowed"),
               )
     不常见的
               (    (400, "parse_error"),
                    (406, "not_acceptable"),
                    (415, "unsupported_media_type"),
                    (429, "throttled")
               )
    """

    response = exception_handler(exc, context)  # 获取本来应该返回的exception的response
    request = context.get("request", None)
    ...


    if response is not None:
        if response.status_code == 403:     # 权限
            pass
        elif response.status_code == 401:   # 是否登录
            pass
        elif response.status_code == 404:   # 资源未找到
            response.data['code'] = org_status_code.NOTFOUNDERROR_CODE.get("code", None)
            response.data['errMsg'] = org_status_code.NOTFOUNDERROR_CODE.get("detail", None)
            del response.data['detail']
        elif response.status_code == 405:  # 方法不允许
            pass
        elif response.status_code == 400:   # 重写django自带的序列化错误以及自定义的序列化错误
            response = process_400_BAD_REQUEST(response)
        else:
            pass

    return response


def process_400_BAD_REQUEST(response):
    """映射自带错误和返回自定义错误"""
    old_data = response.data
    for k, v in old_data.items():
        new_data = {}
        # 框架自带错误码(常见已知的)
        if v[0].code == "required":
            new_data['code'] = org_status_code.NOTNULL_CODE.get("code", None)
            new_data['errMsg'] = org_status_code.NOTNULL_CODE.get("detail", None).format(k)

        elif v[0].code == "invalid":
            new_data['code'] = org_status_code.TYPEERROR_CODE.get("code", None)
            new_data['errMsg'] = org_status_code.TYPEERROR_CODE.get("detail", None).format(k, v[0])

        elif v[0].code == "incorrect_type":  # 类型错误
            new_data['code'] = org_status_code.TYPEERROR_CODE.get("code", None)
            new_data['errMsg'] = org_status_code.TYPEERROR_CODE.get("detail", None).format(k, v[
                0])  # "{0}字段{1}".format(k, v[0])

        elif v[0].code == "does_not_exist":  # 外键对象不存在
            new_data['code'] = org_status_code.FOREIGNKEYNOEXISTED_CODE.get("code", None)
            new_data['errMsg'] = org_status_code.FOREIGNKEYNOEXISTED_CODE.get("detail", None).format(k, v[
                0])  # "{0}字段{1}".format(k, v[0])

        elif v[0].code == "unique":  # 已存在
            new_data['code'] = org_status_code.EXISTED_CODE.get("code", None)
            new_data['errMsg'] = org_status_code.EXISTED_CODE.get("detail", None)

        elif v[0].code == "max_length":  # 最大长度
            new_data['code'] = org_status_code.MAXLENGTHERROR_CODE.get("code", None)

            new_data['errMsg'] = v[0].replace("这个", k)

        elif v[0].code == "min_length":  # 最小长度
            new_data['code'] = org_status_code.MINLENGTHERROR_CODE.get("code", None)
            new_data['errMsg'] = v[0].replace("这个", k)

        # 自定义错误码以及未知的错误码
        else:
            if isinstance(v[0].code, int):  # 自定义的错误
                new_data['code'] = v[0].code
                new_data['errMsg'] = v[0]
            else:
                response.data = old_data
                return response
        response.data = new_data
        return response
  • settings.py配置
REST_FRAMEWORK = {
    'DATETIME_FORMAT': '%Y/%m/%d %H:%M:%S',
    'JWT_ALLOW_REFRESH': True,
    'DEFAULT_AUTHENTICATION_CLASSES': (
        "rest_framework_jwt.authentication.JSONWebTokenAuthentication",
        # 'utils.authentication.CustomAuthenticate',  # 自定义 JSON Token Authentication
        # 'rest_framework.authentication.BasicAuthentication',
        # 'rest_framework.authentication.SessionAuthentication',
    ),
    'EXCEPTION_HANDLER': 'utils.exceptions.custom_exception_handler',   # 自定义重写的异常处理返回
    'SEARCH_PARAM': 'kw',
}

results matching ""

    No results matching ""