Django middleware portion of source code analysis

Middleware, source code analysis

Introduction to Middleware

Middleware is a frame-level requests and responses Django hooks are used. It is a lightweight, low-level plug-in system, for changing the input and output of Django globally. Each middleware component is responsible for doing some specific function.

But because of its impact is global, so it is necessary to use caution, improper use can affect performance.

Middleware can help us to do some additional work performed before and after view function is executed, it is essentially a custom class, a class defines several methods, Django framework to carry out these requests at a specific time method.

Middleware can be defined in the following five main functions to make the hook processing of the input output request:

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

Their main role see the official documentation .

Trigger When these five hook functions may be found in the following figure.

Note: The above said order is registered in the order in middleware settings file list.

Source code analysis

How do I deal with middleware will make the above order, more curious, then I went to study at Django source.

First. At initial startup, the Django range of environmental configurations, including wsgi implement protocols, including initialization middleware component. Middleware initialization function in this entry.

Enter the load_middlewarefunction, you can see that we can customize the hook function in here, on the list of five inside. Then determine settings inside MIDDLEWAREif an item is empty, empty words will go django.conf.global_settings.pyinside the default configuration file is loaded middle piece default middleware only two below.

# django.conf.global_settings.py
MIDDLEWARE_CLASSES = [
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
]

MIDDLEWARE = None

We generally do not comment out seven default middleware under the project, so the above code will go elsebranch, where the elsebranch is the core middleware components of the initialization logic. The last place will all middleware self._middleware_chainThis middleware processing chain in here thinking I design it took quite some time to want to understand.

Then expand elseblock, to the core part. Listed below inside the else part of the source code.

        else:
            # 这里是将handler赋初始值为self._get_response, 这个函数是用来匹配请求url与调用视图函数
            # 并应用view, exception, template_response中间件.
            handler = convert_exception_to_response(self._get_response)
            # 接下来一段代码比较难理解, 但确是设计的精髓.
            # 首先, 遍历我们配置的中间件列表, 只不过这里是逆序遍历, 至于为什么, 往下看就知道了
            for middleware_path in reversed(settings.MIDDLEWARE):
                # 这里是将我们配置的字符串形式的中间件类通过反射解析成类. 原理最后会简单分析
                middleware = import_string(middleware_path)
                try:
                    # 将中间件类实例化为一个对象, 这里把上一个handler当做参数
                    # 这也是能够将中间件通过一个初始化对象链式调用的精髓. 下面会有解释
                    mw_instance = middleware(handler)
                except MiddlewareNotUsed as exc:
                    if settings.DEBUG:
                        if six.text_type(exc):
                            logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
                        else:
                            logger.debug('MiddlewareNotUsed: %r', middleware_path)
                    continue

                # 实例化对象为None, 因为中间件还可以是函数形式
                if mw_instance is None:
                    raise ImproperlyConfigured(
                        'Middleware factory %s returned None.' % middleware_path
                    )
                # 将process_view方法添加到_view_middleware列表的开头
                if hasattr(mw_instance, 'process_view'):
                    self._view_middleware.insert(0, mw_instance.process_view)
                # 将process_template_response方法添加到_template_response_middleware列表的末尾
                # 这里也能解释为什么处理模板以及下面的异常时逆序(按照注册顺序逆序)处理的
                if hasattr(mw_instance, 'process_template_response'):
                    self._template_response_middleware.append(mw_instance.process_template_response)
                if hasattr(mw_instance, 'process_exception'):
                    self._exception_middleware.append(mw_instance.process_exception)
                # 将当前中间件实例化对象重新绑定给handler变量
                handler = convert_exception_to_response(mw_instance)

        # 最后这个handler指向的是中间件列表的第一个实例对象
        self._middleware_chain = handler

After the above analysis, after reading this should still difficult to understand the idea, which is defined in the form of the need to look at object instantiation middleware, see section below the intermediate class is defined;

class MiddlewareMixin(object):
    def __init__(self, get_response=None):
        self.get_response = get_response
        super(MiddlewareMixin, self).__init__()

    def __call__(self, request):
        response = None
        if hasattr(self, 'process_request'):
            response = self.process_request(request)
        if not response:
            response = self.get_response(request)
        if hasattr(self, 'process_response'):
            response = self.process_response(request, response)
        return response

Each class has two basic methods of middleware, will be saved when the object is initialized next get_response, the last call middleware instantiate an object can not stop calling get_response object store, this is the key chain calls can be achieved. The above ideas see the figure demonstrates.

At this time look at the code above, the starting point of the first handler method get_response innermost layer, and then traversing the list from the last intermediate, the handler (in this case, get_response) as a parameter, generates a middleware CommonMiddleware objects, this when the handler points to the new object, and then in cycles, repeat the above operation, the equivalent of one layer of the package.

Finally handler will point to the outermost layer of middleware object. And then assigned to self._middleware_chainthe variable.

When we invoke self._middleware_chain(request)methods when triggered this middleware __call__approach. This time from the outermost layer of middleware, the implementation of process_requestthe method, as long as no response, it will always call the inner layer of middleware variable trigger __call__method, until innermost layer, start processing view related functions. url after the match, before calling the view function, will traverse all process_view method middleware. If the result returned is None, the call to write our view function, if an exception is triggered, goes through the process all process_exception method, if not the method to call process_template_response qualified trigger abnormal triggers similar process_exception method. Finally, the results will return back. and this time will return to layer a layer out from the innermost. this be able to explain middleware order to trigger the hook function.

Here recapture a processing logic innermost, deletion of some

    # django/core/handlers/base.py
    def _get_response(self, request):
        response = None
        # 路由匹配
        if hasattr(request, 'urlconf'):
            urlconf = request.urlconf
            set_urlconf(urlconf)
            resolver = get_resolver(urlconf)
        else:
            resolver = get_resolver()
        resolver_match = resolver.resolve(request.path_info)
        # 这个callback就是我们的视图函数, 后两个是视图函数可能需要的参数
        callback, callback_args, callback_kwargs = resolver_match
        request.resolver_match = resolver_match

        # 应用 view middleware 中间件
        for middleware_method in self._view_middleware:
            response = middleware_method(request, callback, callback_args, callback_kwargs)
            # 只要有response返回, 就立刻停止遍历
            if response:
                break

        if response is None:
            # 给视图函数包装一层
            wrapped_callback = self.make_view_atomic(callback)
            try:
                # 这里是调用视图函数
                response = wrapped_callback(request, *callback_args, **callback_kwargs)
            except Exception as e:
                # 有异常就进入exception view处理
                response = self.process_exception_by_middleware(e, request)
                
        # 这个不常用的process_template_response功能, 看源码可以清楚的知道为什么
        # 返回的结果为啥需要有render方法了
        elif hasattr(response, 'render') and callable(response.render):
            for middleware_method in self._template_response_middleware:
                response = middleware_method(request, response)
                #  ...
            try:
                response = response.render()
            except Exception as e:
                response = self.process_exception_by_middleware(e, request)
        return response

summary

With the above portion of source code analysis, and finally can understand why middleware is based on this order will process requests and responses. Django This idea is I have not been in contact again, after learning finished very deep feelings, can only sigh the exquisite design of the program to others, I would also like to learn.

Guess you like

Origin www.cnblogs.com/yscl/p/11593279.html