在Django中间件处对API进行统一处理

Django中对异常的处理

Django中对request的处理

  • 首先执行process_request函数,然后在执行视图函数之前执行process_view函数,再执行视图函数,最后执行process_response函数
  • process_request只返回None,所有中间件的process_request执行完之后,就匹配路由,找到对应的视图函数,在执行视图函数之前先执行中间件的 process_view函数
  • 如果process_view返回 None,就继续执行后续的中间件的process_view方法,执行完所有的process_view函数之后执行视图函数
  • 如果其中有个 process_view 返回了 HttpResponse,就不执行后续的 process_view 函数,会跳到第一个 process_response 函数,并继续往下执行

图片说明

中间件(类)中5种方法

中间件中可以定义5个方法:

  • process_request(request)
  • process_view(request, view_func, view_args, view_kwargs)
  • process_exception(request, exception)
  • process_template_response(request, response)
  • process_response(request, response)

process_request

  1. 中间件在收到request请求之后执行
  2. 按照settings.pyMIDDLEWARE_CLASSES的顺序,顺序执行
  3. 如果该函数返回None,继续执行后面的中间件的process_request方法
  4. 如果该函数返回HttpResponse,则不再继续执行后面的中间件的process_request方法

process_view

  1. 执行完所有中间件的process_request方法
  2. urls.py中找到对应视图函数
  3. 拿到视图函数的名称、参数,在执行视图函数之前执行
  4. 如果返回None,则继续执行后面的中间件的process_view函数,然后执行下昂应的视图函数
  5. 如果返回HttpResponse,则不执行后续的process_view函数,也不执行视图函数,然后执行所有的response中间件

process_exception

  1. 执行视图函数的过程中如果引发异常,则按照settings.pyMIDDLEWARE_CLASSES的顺序,倒序执行process_exception方法
  2. 如果返回None,继续执行下一个中间件的process_exception方法
  3. 如果返回HttpReponse对象,则该中间件上方其他中间件的process_exception方法不会被调用
  4. 一旦其中某个中间件有返回值,则调用template_responseresponse中间件
    ,否则启动默认的异常处理

最后半句个人理解:如果如果所有中间件的process_exception方法都执完后还没有返回值,则启动默认的异常处理

process_template_response

  1. 在视图函数执行结束之后执行
  2. response是Django视图或者某一中间件的返回值(TemplateResponse对象或等价)
  3. 只有response实现了render方法才会执行
  4. 一旦所有的中间件的template_response被执行完,则调用render方法
  5. 按照中间件的顺序,倒序执行

process_response

  1. 在视图函数执行结束之后执行
  2. 必须有返回值,且返回类型必须是HttpResponse对象
  3. 按照中间件的顺序,倒序执行

代码实现

文件

  • middleware.py

    from django.utils.deprecation import MiddlewareMixin
    from django.models import models
    from django.core.serializers.json import DjangoJSONEncoder
    from django.http import JsonResponse
    class MyMiddleware(MiddlewareMixin):
      def process_exception(self, request, exception):
          if not isinstance(exception, BaseException):
              if settings.DEBUG:
                  return JsonResponse({'result': '', 'msg': str(exception), 'status': 1000})
              else:
                  return JsonResponse(UnknownException().as_dict())
          else:
              return JsonResponse(exception.as_dict())
    
      def process_response(self, request, response):
          procese_type = (list, tuple, dict, str, int)
          if isinstance(response, models.Model):
              response = str(response)
          if isinstance(response, procese_type):
              ret = {'result': response, 'msg': 'success', 'status': 200}
              return JsonResponse(ret, encoder=DjangoJSONEncoder)
          else:
              return response
  • exceptions.py

    from abc import ABCMeta
    from .message import ErrorMsg
    class InterFaceAsDictInterFace:
      def as_dict(self):
          ret = {'result': '', 'msg': getattr(self, '__msg__', ''), 'status': getattr(self, '__status__', '')}
          return ret
    class BaseException(Exception, InterFaceAsDictInterFace):
      __metaclass__ = ABCMeta
    
      def __init__(self, msg=None):
          super(BaseException, self).__init__()
          if msg is not None:
              self.__msg__ = msg
    class UnknownException(InterFaceAsDictInterFace):
      __status__ = 1000
      __msg__ = ErrorMsg.UNKNOWN_EXCEPTION
    class MyException(BaseException):
      __status__=1001
      __msg__=ErrorMsg.MY_EXCEPTION
  • message.py

    from django.utils.translation import gettext as _
    class ErrorMsg:
      UNKNOWN_EXCEPTION= _('Unknown exception.')
      MY_EXCEPTION = _('Test exception.')

修改settings文件

修改setting中的MIDDLEWARE_CLASSES变量

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'middleware.MyMiddleware',
]

Todo

对前端Post请求进行参数校验

目前想出来了两种策略(假设post_json为序列化后的字典):

  1. 视图函数中使用get从字典中获取参数,判断required的参数是否为空,raise自定义的异常,如:

    # exception.py
    ...
    class ValidationError(BaseException):
     __msg__ = ErrorMsg.INVALID_ARGUMENT
     __status__ = 1001
    ...
    
    # message.py
    class ErrorMsg:
     UNKNOWN_EXCEPTION= _('Unknown exception.')
     INVALID_ARGUMENT = _('Invalid arguments.')
     REQUIRED_ARGUMENT = _('A {0} argument is required.')
    
    # view.py
    def test(request):
     ...
     user_name = post_json.get('username','')# required
     pass_word = post_json.get('password','')# required
     user_type = post_json.get('user_type','')# not required
     if not username or not password:
         raise ValidationError(ErrorMsg.REQUIRED_ARGUMENT.format('username/passswordd'))
     ...
  2. 视图函数中对参数不做校验,只需在中间件添加一句,即可对视图函数中raiseKeyError进行统一处理

    # exception.py
    ...
    class ValidationError(BaseException):
     __msg__ = ErrorMsg.INVALID_ARGUMENT
     __status__ = 1001
    ...
    
    # middleware.py
    ...
    def process_exception(self, request, exception):
    if isinstance(exception, KeyError):
     exception = ValidationError(ErrorMsg.REQUIRED_ARGUMENT.format(exception))
     ...
    
    # message.py
    class ErrorMsg:
     UNKNOWN_EXCEPTION= _('Unknown exception.')
     INVALID_ARGUMENT = _('Invalid arguments.')
     REQUIRED_ARGUMENT = _('A {0} argument is required.')
    
    # view.py
    def test(request):
     ...
     user_name = post_json['user_name'] # required
     user_type = post_json.get('user_type','')# not required
     ...

视图函数返回

目前视图函数必须有返回值,不能为None,还不知道怎么解决

#推荐算法工程师实习#
全部评论
感谢参与牛客创作者计划!欢迎更多牛友来写干货,瓜分5000元奖励~~技术场活动链接:https://www.nowcoder.com/link/czztlqjs
点赞 回复
分享
发布于 2020-11-20 11:10

相关推荐

头像
04-07 00:10
点赞 评论 收藏
转发
5 8 评论
分享
牛客网
牛客企业服务