django middleware 中间件原理概念,源码解读分析
用到的知识点
- 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 中间件原理概念,源码解读分析相关推荐
- 并发编程之 Executor 线程池原理与源码解读
并发编程之 Executor 线程池原理与源码解读 线程是调度 CPU 资源的最小单位,线程模型分为 KLT 模型与 ULT 模型,JVM使用的是 KLT 模型.java线程与 OS 线程保持 1:1 ...
- Spring原理学习系列之三:Spring AOP原理(从源码层面分析)-------上部
引言 本文是Spring原理分析的第三篇博文,主要阐述Spring AOP相关概念,同时从源码层面分析AOP实现原理.对于AOP原理的理解有利于加深对Spring框架的深入理解.同时我也希望可以探究S ...
- PCL:超详细的基于法向量和曲率的区域生长算法原理以及源码解读
---------原理介绍: (1)首先计算出来各点的曲率值,将曲率值按照从小到大的顺序进行排序. (2)设置一空的种子点序列和一个空的聚类数组. (3)选取曲率最小的点放入上述种子点序列中. (4) ...
- Guava RateLimiter算法原理及源码解读
目录 前言 原理 RateLimiter原理 SmoothBursty 关键属性 关键方法 doSetRate reserveEarliestAvailable SmoothWarmingUp War ...
- java并发编程——线程池的工作原理与源码解读
2019独角兽企业重金招聘Python工程师标准>>> 线程池的简单介绍 基于多核CPU的发展,使得多线程开发日趋流行.然而线程的创建和销毁,都涉及到系统调用,比较消耗系统资源,所以 ...
- 面试官-你真的懂computed原理?(源码解读)
要理解 computed 的工作原理,只需要理解下面4个特性 - 特性1:computed默认不执行(因为 lazy 的原因,在新建watcher实例的时候,会将 watcher.value 赋值为 ...
- 并发编程之Executor线程池原理与源码解读
1. 线程池 "线程池",顾名思义就是一个线程缓存,线程是稀缺资源,如果被无限制的创建,不 仅会消耗系统资源,还会降低系统的稳定性,因此Java中提供线程池对线程进行统一分配. 调 ...
- Ribbon负载均衡原理,源码解读
Ribbon负责均衡原理图 源码详解: @LoadBalanced 标记RestTemplate发起的请求,会被loadBalanced拦截和处理 /*** 创建RestTemplate并注入Spri ...
- 线程池的工作原理与源码解读
点击上方"方志朋",选择"置顶或者星标" 你的关注意义重大! 随着cpu核数越来越多,不可避免的利用多线程技术以充分利用其计算能力.所以,多线程技术是服务端开发 ...
最新文章
- Java黑皮书课后题第5章:**5.19(打印金字塔形的数字)编写一个嵌套的for循环,打印下面的输出
- c++成员运算符的重载
- 理解Go语言中的方法和接收者
- 大学c语言程序设计期末考试试卷,大学大一c语言程序设计期末考试试卷及答案.doc...
- 基于JAVA+SpringBoot+Mybatis+MYSQL的社团管理系统
- 让你的Python程序在用户面前以小概率崩溃
- 二维树状数组(水题) POJ1195
- python scrapy框架df_Python - Scrapy 框架
- Ninject.Web.Common,Ninject.MVC3源码分析
- html css:背景图片链接css写法
- 【人脸识别】基于matlab GUI PCA人脸二维码识别(带面板)【含Matlab源码 754期】
- 运筹学 matlab实现运输问题(表上作业法)
- java小项目-继承-接口-Swing窗口(一共5个demo)
- Symbian智能手机特殊号码搜集(转)
- 关于笔记本WLAN无线上网
- 程序员必须 知道的英语单词
- 040-云E办_学习和安装FastDFS以及安装Nginx
- arma模型_R语言: GARCH模型股票交易量的研究道琼斯股票市场指数
- 狗都能看懂的Self-Attention讲解
- 《笨办法学Python》——习题5