用到的知识点

  • wsgi
    搜索应用的入口
  • 闭包,高阶函数递归调用
    中间件实现的关键技术
  • asyncio
    了解异步与同步函数类型转换

原理概念逻辑

引用官方文档

你可以把它想象成一个洋葱:每个中间件类都是一个“层”,它覆盖了洋葱的核心。如果请求通过洋葱的所有层(每一个调用 get_response )以将请求传递到下一层,一直到内核的视图,那么响应将在返回的过程中通过每个层(以相反的顺序)。

如果其中一层决定停止并返回响应而不调用get_response,那么该层(包括视图)中的洋葱层都不会看到请求或响应。响应将只通过请求传入的相同层返回。

源码解读分析

python3.8/site-packages/django/core/handlers/wsgi.py

class WSGIHandler(base.BaseHandler):request_class = WSGIRequestdef __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)self.load_middleware()def __call__(self, environ, start_response):set_script_prefix(get_script_name(environ))signals.request_started.send(sender=self.__class__, environ=environ)request = self.request_class(environ)response = self.get_response(request)response._handler_class = self.__class__status = '%d %s' % (response.status_code, response.reason_phrase)response_headers = [*response.items(),*(('Set-Cookie', c.output(header='')) for c in response.cookies.values()),]start_response(status, response_headers)if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):# If `wsgi.file_wrapper` is used the WSGI server does not call# .close on the response, but on the file wrapper. Patch it to use# response.close instead which takes care of closing all files.response.file_to_stream.close = response.closeresponse = environ['wsgi.file_wrapper'](response.file_to_stream, response.block_size)return response

第6行 self.load_middleware()

读取配置,初始化所有中间件

python3.8/site-packages/django/core/handlers/base.py

class BaseHandler:_view_middleware = None_template_response_middleware = None_exception_middleware = None_middleware_chain = Nonedef 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_responsehandler = convert_exception_to_response(get_response)handler_is_async = is_asyncfor 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 = Falseelse:middleware_is_async = middleware_can_asynctry:# 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)continueelse:handler = adapted_handlerif 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...    def get_response(self, request):"""Return an HttpResponse object for the given HttpRequest."""# Setup default url resolver for this threadset_urlconf(settings.ROOT_URLCONF)response = self._middleware_chain(request)response._resource_closers.append(request.close)if response.status_code >= 400:log_response('%s: %s', response.reason_phrase, request.path,response=response,request=request,)return response...def _get_response(self, request):"""Resolve and call the view, then apply view, exception, andtemplate_response middleware. This method is everything that happensinside the request/response middleware."""response = Nonecallback, callback_args, callback_kwargs = self.resolve_request(request)# Apply view middlewarefor middleware_method in self._view_middleware:response = middleware_method(request, callback, callback_args, callback_kwargs)if response:breakif response is None:wrapped_callback = self.make_view_atomic(callback)# If it is an asynchronous view, run it in a subthread.if asyncio.iscoroutinefunction(wrapped_callback):wrapped_callback = async_to_sync(wrapped_callback)try:response = wrapped_callback(request, *callback_args, **callback_kwargs)except Exception as e:response = self.process_exception_by_middleware(e, request)if response is None:raise# Complain if the view returned None (a common error).self.check_response(response, callback)# If the response supports deferred rendering, apply template# response middleware and then render the responseif hasattr(response, 'render') and callable(response.render):for middleware_method in self._template_response_middleware:response = middleware_method(request, response)# Complain if the template response middleware returned None (a common error).self.check_response(response,middleware_method,name='%s.process_template_response' % (middleware_method.__self__.__class__.__name__,))try:response = response.render()except Exception as e:response = self.process_exception_by_middleware(e, request)if response is None:raisereturn response

第17行 get_response = self._get_response_async if is_async else self._get_response

如果把中间件结构类比洋葱,这次获取的get_response函数就是洋葱内核。

第39行 mw_instance = middleware(adapted_handler)

每次调用增加一层葱皮。

第78行 self._middleware_chain = handler

保存最外层葱皮的引用。

第85行 response = self._middleware_chain(request)

从最外层葱皮启动中间件的解析,既是每次访问初始化Request对象后解析响应的入口点,也是响应逆向返回处理的终点。

第95行

def _get_response(self, request):"""Resolve and call the view, then apply view, exception, andtemplate_response middleware. This method is everything that happensinside the request/response middleware."""response = Nonecallback, callback_args, callback_kwargs = self.resolve_request(request)...

如果所有中间件在拨开内层中间件前都顺利执行且没有返回response,内核_get_response将得到调用,callback, callback_args, callback_kwargs分别是我们在urlconf中定义的视图和参数。
后面就是调用由类定义的中间件的的函数process_view,process_exception,process_template_response。

python3.8/site-packages/django/utils/deprecation.py

官方中间件扩展MiddlewareMixin

 ...def __call__(self, request):# Exit out to async mode, if neededif asyncio.iscoroutinefunction(self.get_response):return self.__acall__(request)response = Noneif hasattr(self, 'process_request'):response = self.process_request(request)response = response or self.get_response(request)if hasattr(self, 'process_response'):response = self.process_response(request, response)return response...

process_request函数在末端中间件调用前顺序调用。

process_response函数在末端中间件完成后逆序调用。
之所以叫末端中间件,因为如果期间有中间件返回response,那么后面的中间件将到不到执行,并逆序解析response完成访问。

django middleware 中间件原理概念,源码解读分析相关推荐

  1. 并发编程之 Executor 线程池原理与源码解读

    并发编程之 Executor 线程池原理与源码解读 线程是调度 CPU 资源的最小单位,线程模型分为 KLT 模型与 ULT 模型,JVM使用的是 KLT 模型.java线程与 OS 线程保持 1:1 ...

  2. Spring原理学习系列之三:Spring AOP原理(从源码层面分析)-------上部

    引言 本文是Spring原理分析的第三篇博文,主要阐述Spring AOP相关概念,同时从源码层面分析AOP实现原理.对于AOP原理的理解有利于加深对Spring框架的深入理解.同时我也希望可以探究S ...

  3. PCL:超详细的基于法向量和曲率的区域生长算法原理以及源码解读

    ---------原理介绍: (1)首先计算出来各点的曲率值,将曲率值按照从小到大的顺序进行排序. (2)设置一空的种子点序列和一个空的聚类数组. (3)选取曲率最小的点放入上述种子点序列中. (4) ...

  4. Guava RateLimiter算法原理及源码解读

    目录 前言 原理 RateLimiter原理 SmoothBursty 关键属性 关键方法 doSetRate reserveEarliestAvailable SmoothWarmingUp War ...

  5. java并发编程——线程池的工作原理与源码解读

    2019独角兽企业重金招聘Python工程师标准>>> 线程池的简单介绍 基于多核CPU的发展,使得多线程开发日趋流行.然而线程的创建和销毁,都涉及到系统调用,比较消耗系统资源,所以 ...

  6. 面试官-你真的懂computed原理?(源码解读)

    要理解 computed 的工作原理,只需要理解下面4个特性 - 特性1:computed默认不执行(因为 lazy 的原因,在新建watcher实例的时候,会将 watcher.value 赋值为 ...

  7. 并发编程之Executor线程池原理与源码解读

    1. 线程池 "线程池",顾名思义就是一个线程缓存,线程是稀缺资源,如果被无限制的创建,不 仅会消耗系统资源,还会降低系统的稳定性,因此Java中提供线程池对线程进行统一分配. 调 ...

  8. Ribbon负载均衡原理,源码解读

    Ribbon负责均衡原理图 源码详解: @LoadBalanced 标记RestTemplate发起的请求,会被loadBalanced拦截和处理 /*** 创建RestTemplate并注入Spri ...

  9. 线程池的工作原理与源码解读

    点击上方"方志朋",选择"置顶或者星标" 你的关注意义重大! 随着cpu核数越来越多,不可避免的利用多线程技术以充分利用其计算能力.所以,多线程技术是服务端开发 ...

最新文章

  1. Java黑皮书课后题第5章:**5.19(打印金字塔形的数字)编写一个嵌套的for循环,打印下面的输出
  2. c++成员运算符的重载
  3. 理解Go语言中的方法和接收者
  4. 大学c语言程序设计期末考试试卷,大学大一c语言程序设计期末考试试卷及答案.doc...
  5. 基于JAVA+SpringBoot+Mybatis+MYSQL的社团管理系统
  6. 让你的Python程序在用户面前以小概率崩溃
  7. 二维树状数组(水题) POJ1195
  8. python scrapy框架df_Python - Scrapy 框架
  9. Ninject.Web.Common,Ninject.MVC3源码分析
  10. html css:背景图片链接css写法
  11. 【人脸识别】基于matlab GUI PCA人脸二维码识别(带面板)【含Matlab源码 754期】
  12. 运筹学 matlab实现运输问题(表上作业法)
  13. java小项目-继承-接口-Swing窗口(一共5个demo)
  14. Symbian智能手机特殊号码搜集(转)
  15. 关于笔记本WLAN无线上网
  16. 程序员必须 知道的英语单词
  17. 040-云E办_学习和安装FastDFS以及安装Nginx
  18. arma模型_R语言: GARCH模型股票交易量的研究道琼斯股票市场指数
  19. 狗都能看懂的Self-Attention讲解
  20. 《笨办法学Python》——习题5

热门文章

  1. 腾讯云后端 15 连问
  2. 【docker】docker常用命令汇总
  3. 36个JavaScript特效教程,学完即精通
  4. js替换字符串中全部“-”
  5. 完全图解scrollLeft,scrollWidth,clientWidth,offsetWidth
  6. 新手入门:介绍JSP中request属性的用法
  7. Java高级技术之Gradle
  8. git命令看一这篇就够了
  9. cesium.js入门(一)
  10. 计算机视觉(十五):综合案例:垃圾分类