1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > django middleware 中间件原理概念 源码解读分析

django middleware 中间件原理概念 源码解读分析

时间:2019-01-14 08:44:38

相关推荐

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完成访问。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。