Django中间件的源码解析流程(上)——中间件载入的前置

目录

[1. ​前言​](#1. 前言)

[2. 请求的入口](#2. 请求的入口)

[3. 中间件加载的入口](#3. 中间件加载的入口)

[4. 源码中的闭包实现](#4. 源码中的闭包实现)

[5. 最后](#5. 最后)


1. 前言********

哈喽,大家好,我是小K,今天咋们分享的内容是:在学会Django中间件之后, 我们继续深入底层源码。

在执行中间件时请求到来总是从前往后逐一匹配,但当响应返回时,执行的中间件顺序往往是反着执行的。

在Django中间件中,采用了多层闭包的形式,以来达到逐一执行中间件,本篇文章主要是从请求的入口开始,简单介绍一下前置。

如果还不会使用中间件的小伙伴可以跳转到这里:

Django之五种中间件定义类型

2. 请求的入口

在同步请求中,请求的入口其实就是 wsgi.py 文件, 不清楚的可以跳转这里,看第2点:

Django源码之路由匹配(下)------图解逐步分析底层源码

3. 中间件加载的入口

在请求到来时,最后返回的是一个WSGIHandler的一个对象

python 复制代码
application = get_wsgi_application()


def get_wsgi_application():
    django.setup(set_prefix=False)
    return WSGIHandler()


class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.load_middleware()

在加载对象时,肯定是少不了执行__init__的初始化方法的

复制代码
self.load_middleware()   # 这其实就是加载中间件的方法

在执行load_middleware()函数时,首先是在自己的类中寻找,如果没有就去父类中寻找

显然,这里的代码是需要我们去父类BaseHandler类中寻找的


这里BaseHandler类和load_middleware的源码:

python 复制代码
class BaseHandler:
    _view_middleware = None
    _template_response_middleware = None
    _exception_middleware = None
    _middleware_chain = None

    def load_middleware(self, is_async=False):
        """
        Populate middleware lists from settings.MIDDLEWARE.

        Must be called after the environment is fixed (see __call__ in subclasses).
        """
        self._view_middleware = []
        self._template_response_middleware = []
        self._exception_middleware = []

        get_response = self._get_response_async if is_async else self._get_response
        handler = convert_exception_to_response(get_response)
        handler_is_async = is_async
        for middleware_path in reversed(settings.MIDDLEWARE):
            middleware = import_string(middleware_path)
            middleware_can_sync = getattr(middleware, "sync_capable", True)
            middleware_can_async = getattr(middleware, "async_capable", False)
            if not middleware_can_sync and not middleware_can_async:
                raise RuntimeError(
                    "Middleware %s must have at least one of "
                    "sync_capable/async_capable set to True." % middleware_path
                )
            elif not handler_is_async and middleware_can_sync:
                middleware_is_async = False
            else:
                middleware_is_async = middleware_can_async
            try:
                # Adapt handler, if needed.
                adapted_handler = self.adapt_method_mode(
                    middleware_is_async,
                    handler,
                    handler_is_async,
                    debug=settings.DEBUG,
                    name="middleware %s" % middleware_path,
                )
                mw_instance = middleware(adapted_handler)
            except MiddlewareNotUsed as exc:
                if settings.DEBUG:
                    if str(exc):
                        logger.debug("MiddlewareNotUsed(%r): %s", middleware_path, exc)
                    else:
                        logger.debug("MiddlewareNotUsed: %r", middleware_path)
                continue
            else:
                handler = adapted_handler

            if mw_instance is None:
                raise ImproperlyConfigured(
                    "Middleware factory %s returned None." % middleware_path
                )

            if hasattr(mw_instance, "process_view"):
                self._view_middleware.insert(
                    0,
                    self.adapt_method_mode(is_async, mw_instance.process_view),
                )
            if hasattr(mw_instance, "process_template_response"):
                self._template_response_middleware.append(
                    self.adapt_method_mode(
                        is_async, mw_instance.process_template_response
                    ),
                )
            if hasattr(mw_instance, "process_exception"):
                # The exception-handling stack is still always synchronous for
                # now, so adapt that way.
                self._exception_middleware.append(
                    self.adapt_method_mode(False, mw_instance.process_exception),
                )

            handler = convert_exception_to_response(mw_instance)
            handler_is_async = middleware_is_async

        # Adapt the top of the stack, if needed.
        handler = self.adapt_method_mode(is_async, handler, handler_is_async)
        # We only assign to this when initialization is complete as it is used
        # as a flag for initialization being complete.
        self._middleware_chain = handler

ok,我们现在着重来分析load_middleware的源码

4. 源码中的闭包实现

先来看看什么是闭包:

Python奇幻之旅(从入门到入狱基础篇)------附相关资料【中】-CSDN博客

这篇文章里面有一部分介绍了闭包和装饰器

现在来看具体的闭包实现函数:

python 复制代码
def convert_exception_to_response(get_response):
    if iscoroutinefunction(get_response):

        @wraps(get_response)
        async def inner(request):
            try:
                response = await get_response(request)
            except Exception as exc:
                response = await sync_to_async(
                    response_for_exception, thread_sensitive=False
                )(request, exc)
            return response

        return inner
    else:
        @wraps(get_response)
        def inner(request):
            try:
                response = get_response(request)
            except Exception as exc:
                response = response_for_exception(request, exc)
            return response

        return inner

最后返回了一个inner函数, 而这个inner调用的是get_response()

最后都被封装进了handler里面

5. 最后

本文只是简单介绍了一下中间件加载的位置,下一篇文章将采用长篇的形式详细讲解中间件是如何通过闭包的形式载入的。今天就先到这里啦!

相关推荐
hummhumm12 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
杜小满16 分钟前
周志华深度森林deep forest(deep-forest)最新可安装教程,仅需在pycharm中完成,超简单安装教程
python·随机森林·pycharm·集成学习
隔着天花板看星星1 小时前
Kafka-Consumer理论知识
大数据·分布式·中间件·kafka
隔着天花板看星星1 小时前
Kafka-副本分配策略
大数据·分布式·中间件·kafka
databook1 小时前
『玩转Streamlit』--布局与容器组件
python·机器学习·数据分析
nuclear20112 小时前
使用Python 在Excel中创建和取消数据分组 - 详解
python·excel数据分组·创建excel分组·excel分类汇总·excel嵌套分组·excel大纲级别·取消excel分组
Lucky小小吴2 小时前
有关django、python版本、sqlite3版本冲突问题
python·django·sqlite
GIS 数据栈3 小时前
每日一书 《基于ArcGIS的Python编程秘笈》
开发语言·python·arcgis
爱分享的码瑞哥3 小时前
Python爬虫中的IP封禁问题及其解决方案
爬虫·python·tcp/ip
wenyue11213 小时前
Ease Monitor 会把基础层,中间件层的监控数据和服务的监控数据打通,从总体的视角提供监控分析
运维·中间件·监控