BaseHandler 详解

BaseHandler 在 django.core.handlers.base.py 中定义, 有两个核心的成员方法不得不提, 里面就涉及了中间件的信息:

# 好经典的 handler
class BaseHandler(object):# Changes that are always applied to a response (in this order).response_fixes = [http.fix_location_header,http.conditional_content_removal,http.fix_IE_for_attach,http.fix_IE_for_vary,]初始化函数, 初始化请求中间件, 视图中间件, 模版中间件, 响应中间件和异常中间件.def __init__(self):self._request_middleware = self._view_middleware =self._template_response_middleware =self._response_middleware =self._exception_middleware = None  视图, 模版相应, 相应, 异常中间件, 请求中间件根据 mysite.settings.py 中的 `MIDDLEWARE_CLASSES` 添加所有的中间件.def load_middleware(self):"""Populate middleware lists from settings.MIDDLEWARE_CLASSES.从 settings 中加载各种中间件Must be called after the environment is fixed (see __call__ in subclasses)."""# 初始化四种中间件self._view_middleware = []self._template_response_middleware = []self._response_middleware = []self._exception_middleware = []# 临时的请求中间件, 因为在加入中间件的过程中, 可能会出现异常, 而出现异常都导致加载中间件的不成功, 因此将 self._request_middleware 的赋值放在最后, 表示已经成功.request_middleware = []# settings.MIDDLEWARE_CLASSES 设置项指定需要预装的中间件for middleware_path in settings.MIDDLEWARE_CLASSES:try:mw_module, mw_classname = middleware_path.rsplit('.', 1)except ValueError:raise exceptions.ImproperlyConfigured('%s isn\'t a middleware module' % middleware_path)try:尝试导入中间件所在模块.mod = import_module(mw_module)except ImportError as e:raise exceptions.ImproperlyConfigured('Error importing middleware %s: "%s"' % (mw_module, e))try:尝试得到某种中间件类mw_class = getattr(mod, mw_classname)except AttributeError:raise exceptions.ImproperlyConfigured('Middleware module "%s" does not define a "%s" class' % (mw_module, mw_classname))try:尝试实例化mw_instance = mw_class()except exceptions.MiddlewareNotUsed:continue和 urllib 的处理方法类似: 请求预处理, 视图处理?, 模版处理, 相应处理, 错误处理(详见我的 urllib 源码剖析)if hasattr(mw_instance, 'process_request'):# 这里 request_middleware 用的是 append(), 这里是有讲究的:# django 规定, 多个请求中间件调用的次序是其出现的次序, 下同request_middleware.append(mw_instance.process_request)if hasattr(mw_instance, 'process_view'):self._view_middleware.append(mw_instance.process_view)if hasattr(mw_instance, 'process_template_response'):# 这里 _template_response_middleware 用的是 insert() 头插法, 这里是有讲究的:# django 规定, 多个模版相应中间件调用的次序是其出现次序的逆序, 下同self._template_response_middleware.insert(0, mw_instance.process_template_response)if hasattr(mw_instance, 'process_response'):self._response_middleware.insert(0, mw_instance.process_response)if hasattr(mw_instance, 'process_exception'):self._exception_middleware.insert(0, mw_instance.process_exception)# We only assign to this when initialization is complete as it is used# as a flag for initialization being complete.# 结束的标识, 表明中间件加载成功self._request_middleware = request_middleware# 处理请求的函数, 并返回 responsedef get_response(self, request):"Returns an HttpResponse object for the given HttpRequest"根据请求, 得到响应try:为该线程提供默认的 url 处理器# Setup default url resolver for this thread, this code is outside# the try/except so we don't get a spurious "unbound local# variable" exception in the event an exception is raised before# resolver is set#ROOT_URLCONF = 'mysite.urls'urlconf = settings.ROOT_URLCONF# set_urlconf() 会设置 url 配置即 settings.ROOT_URLCONFurlresolvers.set_urlconf(urlconf)# 实例化 RegexURLResolver, 暂且将其理解为一个 url 的匹配处理器, 下节展开resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)try:response = None# Apply request middleware 调用请求中间件for middleware_method in self._request_middleware:response = middleware_method(request)# 如果此 response 有效, 即不走下面的逻辑if response:break# 如果没有结果if response is None:# 尝试 request 中是否有 urlconf, 一般没有, 可以忽略此段代码!!!if hasattr(request, 'urlconf'):# Reset url resolver with a custom urlconf. 自定义的 urlconfurlconf = request.urlconfurlresolvers.set_urlconf(urlconf)resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)# 调用 RegexURLResolver.resolve(), 可以理解为启动匹配的函数; 返回 ResolverMatch 实例resolver_match = resolver.resolve(request.path_info)# resolver_match 对象中存储了有用的信息, 譬如 callback 就是我们在 views.py 中定义的函数.callback, callback_args, callback_kwargs = resolver_match# 将返回的 resolver_match 挂钩到 requestrequest.resolver_match = resolver_match# Apply view middleware 调用视图中间件for middleware_method in self._view_middleware:response = middleware_method(request, callback, callback_args, callback_kwargs)# 如果此 response 有效, 即不走下面的逻辑if response:break# response 还是为空if response is None:try:# 这里调用的是真正的处理函数, 我们一般在 view.py 中定义这些函数response = callback(request, *callback_args, **callback_kwargs)except Exception as e:# If the view raised an exception, run it through exception# middleware, and if the exception middleware returns a# response, use that. Otherwise, reraise the exception.# 出现异常, 调用异常中间件for middleware_method in self._exception_middleware:response = middleware_method(request, e)# 如果此 response 有效, 即不走下面的逻辑if response:breakif response is None:raise# response 还是为空, 可能就要异常了# Complain if the view returned None (a common error).if response is None:if isinstance(callback, types.FunctionType):    # FBVview_name = callback.__name__else:                                           # CBVview_name = callback.__class__.__name__ + '.__call__'raise ValueError("The view %s.%s didn't return an HttpResponse object." % (callback.__module__, view_name))# If the response supports deferred rendering, apply template# response middleware and the render the response 如果 response 实现了 render, 那么渲染返回.if hasattr(response, 'render') and callable(response.render):for middleware_method in self._template_response_middleware:response = middleware_method(request, response)response = response.render()except http.Http404 as e:logger.warning('Not Found: %s', request.path,extra={'status_code': 404,'request': request})# 如果是调试下, 直接要返回 404 页面if settings.DEBUG:response = debug.technical_404_response(request, e)else:try:# 非调试模式下, 获取 url 处理器的默认 404 处理callback, param_dict = resolver.resolve404()response = callback(request, **param_dict)except:signals.got_request_exception.send(sender=self.__class__, request=request)response = self.handle_uncaught_exception(request, resolver, sys.exc_info())# 访问拒绝except exceptions.PermissionDenied:logger.warning('Forbidden (Permission denied): %s', request.path,extra={'status_code': 403,'request': request})try:callback, param_dict = resolver.resolve403()response = callback(request, **param_dict)except:signals.got_request_exception.send(sender=self.__class__, request=request)response = self.handle_uncaught_exception(request,resolver, sys.exc_info())except SystemExit:# Allow sys.exit() to actually exit. See tickets #1023 and #4701raiseexcept: # Handle everything else, including SuspiciousOperation, etc.# Get the exception info now, in case another exception is thrown later.signals.got_request_exception.send(sender=self.__class__, request=request)response = self.handle_uncaught_exception(request, resolver, sys.exc_info())finally:# Reset URLconf for this thread on the way out for complete# isolation of request.urlconf 重置, 因为前面有两种 url resolver 的可能, 拒绝混淆urlresolvers.set_urlconf(None)try:# Apply response middleware, regardless of the response 调用响应中间件for middleware_method in self._response_middleware:response = middleware_method(request, response)response = self.apply_response_fixes(request, response)except: # Any exception should be gathered and handledsignals.got_request_exception.send(sender=self.__class__, request=request)response = self.handle_uncaught_exception(request, resolver, sys.exc_info())return responsedef handle_uncaught_exception(self, request, resolver, exc_info):"""处理未能捕捉的错误Processing for any otherwise uncaught exceptions (those that willgenerate HTTP 500 responses). Can be overridden by subclasses who wantcustomised 500 handling. 子类中可以重写 500 状态的处理Be *very* careful when overriding this because the error could becaused by anything, so assuming something like the database is alwaysavailable would be an error."""if settings.DEBUG_PROPAGATE_EXCEPTIONS:raiselogger.error('Internal Server Error: %s', request.path,exc_info=exc_info,extra={'status_code': 500,'request': request})调试模式特殊处理if settings.DEBUG:return debug.technical_500_response(request, *exc_info)# If Http500 handler is not installed, re-raise last exception 如果http500 处理器都没有安装, 可能会崩溃if resolver.urlconf_module is None:six.reraise(*exc_info)# Return an HttpResponse that displays a friendly error message.#这是自定义的 500 处理器callback, param_dict = resolver.resolve500()return callback(request, **param_dict)def apply_response_fixes(self, request, response):"""Applies each of the functions in self.response_fixes to the request andresponse, modifying the response in the process. Returns the newresponse."""for func in self.response_fixes:response = func(request, response)return response

总结

load_middleware() 函数会根据 mysite.settings.py 中的 MIDDLEWARE_CLASSES 导入所有的中间件. 在 eclipse + pydev 创建 django 的默认设置当中就有默认的中间件:

MIDDLEWARE_CLASSES = ('django.middleware.common.CommonMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware',# Uncomment the next line for simple clickjacking protection:# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

每一个中间件都是一个类, 其内部会实现 process_request(),process_view(),process_template_response(),process_response() 或者 process_exception() 方法. 不一定都实现, 看需求. 而这些方法如果存在, 都会被保存响应的函数列表中, 待将来调用.

get_response() 方法, 中间件调用执行的顺序是请求中间件, 视图中间件, 模版中间件, 异常中间件(可选), 响应中间件. 习惯上, 我把这些简称为请求预处理和响应善后处理.get_response() 返回了 response, 但一长串的 url 是如何匹配的, 并且自己在 views.py 中的函数是在什么时候调用的?

Django 源码: 中间件(middleware)相关推荐

  1. Django源码分析5:session会话中间件分析

    django源码分析 本文环境python3.5.2,django1.10.x系列 1.这次分析django框架中的会话中间件. 2.会话保持是目前框架都支持的一个功能,因为http是无状态协议,无法 ...

  2. Django源码分析4:staticfiles静态文件处理中间件分析

    django源码分析 本文环境python3.5.2,django1.10.x系列1.在上一篇文章中已经分析过handler的处理过程,其中load_middleware就是将配置的中间件进行初始化, ...

  3. Django源码分析6:auth认证及登陆保持

    django源码分析 本文环境python3.5.2,django1.10.x系列 1.这次分析django框架中登陆认证与接口权限检查. 2.在后端开发中,难免会对接口进行权限验证,其中对于接口是否 ...

  4. Django源码分析3:处理请求wsgi分析与视图View

    django源码分析 本文环境python3.5.2,django1.10.x系列 根据前上一篇runserver的博文,已经分析了本地调试服务器的大致流程,现在我们来分析一下当runserver运行 ...

  5. django源码简析——后台程序入口

    django源码简析--后台程序入口 这一年一直在用云笔记,平时记录一些tips或者问题很方便,所以也就不再用博客进行记录,还是想把最近学习到的一些东西和大家作以分享,也能够对自己做一个总结.工作中主 ...

  6. django源码阅读

    最近再看django-bootstrap-toolkit,一直困惑于静态文件的路径问题.所以只能从源码入手了.    从manage.py开始. manage.py 比较简单就几句话. #!/usr/ ...

  7. Django源码分析10:makemigrations命令概述

    django源码分析 本文环境python3.5.2,django1.10.x系列 django源码分析-makemigrations命令概述 Django项目中的数据库管理命令就是通过makemig ...

  8. Django源码分析9:model.py表结构的初始化概述

    django源码分析 本文环境python3.5.2,django1.10.x系列 django源码分析-model概述 Django项目中提供了内置的orm框架,只需要在models.py文件中添加 ...

  9. Django源码分析8:单元测试test命令浅析

    django源码分析 本文环境python3.5.2,django1.10.x系列 django源码分析-test命令分析 Django项目中提供了,test命令行命令来执行django的单元测试,该 ...

最新文章

  1. windows 如何安装oracle 补丁包,Windows Server 2003 上安装 Oracle10g(10.2.0.1)并升级 至补丁(10.2.0.4) 图解...
  2. oracle 最小系统,基于最小化CENTOS6.6最小化安装,oracle 11g 数据安装过程!
  3. 如何在QT中读取串口数据
  4. SAP Spartacus internationalization ( i18n ) 翻译问题的排错指南
  5. 鼓励参与计算机考试宣传标语,诚信考试的宣传标语(精选60条)
  6. 基于jsp+mysql+Spring+hibernate+在线学习交流论坛平台
  7. Unitest框架的使用(四)HTMLTestRunner输出测试报告
  8. AndroidSDK下载
  9. github windows系统监控_windows快速制作U盘启动工具Rufus
  10. Element UI 中国省市区级联数据
  11. 论文解读:预测lncRNA的相互作用生物分子类型:一种集成深度学习方法
  12. Mac 下修改文件的 md5 值
  13. 华为OD机试真题 Python 实现【数字涂色】
  14. Python爬取12306车票信息
  15. 二、IAR for ARM中STM32项目创建及其启动文件分析
  16. 苏轼《定风波》词两首
  17. McAfee阻止邮件发送功能
  18. GoLang封装常用工具方法
  19. HTML(二)语义化、核心常用元素
  20. 异常检测阅读笔记《Inpainting Transformer for Anomaly Detection》CVPR 2021

热门文章

  1. java 如何创建项目 包 类
  2. 搭建云蜜罐捕捉在野0day...
  3. 安装win7和win10或win7和linux双系统
  4. 理解运筹学||数学规划
  5. 如何关闭searchIndexer.exe进程
  6. 泛域名解析和域名解析的区别+DNS
  7. 【AHK】如何实现通达信电脑端 核按钮清仓
  8. 华为弱了?骁龙865Plus加持,Galaxy Tab S7配置曝光
  9. 戴尔U盘重装系统Win10步骤和详细教程
  10. “河道”收窄的水滴公司,要奔往哪片“汪洋”