原创作者: hideto
阅读:2072次
评论:0条
更新时间:2011-06-01
The Django Book:第16章 中间件
有时你需要对每个Django处理的请求运行一段代码,该代码可能需要在视图处理它之前修改请求,或者可能日志为了调试而
记录关于请求的信息等等
Django的中间件框架本质上是进入Django的请求/应答处理的钩子,它是全局改变Django的输入和输出的轻量的,低级的"插
件"系统
每个中间件组件负责一些专有的功能,如果你线性的阅读本书,则你已经看到中间件好几次了:
1,我们在第12章看到的所有俏皮的会话和用户工具可能由一些小块的中间件构成(更明确的,中间件使得你在视图中可以得
到request.session和request.user)
2,第12章讨论的站点宽度的缓存事实上只是一些中间件,如果视图的应答已经被缓存则它们是调用你的视图方法的捷径
3,第15章的flatpages,redirects和csrf这些贡献的apps都是通过使用中间件组件来施展它们的魔力
本章深入了解什么是中间件以及它怎样工作,并解释了你怎样写你自己的中间件
什么是中间件?
中间件事实上难以置信的简单,一个中间件组件是简单的遵守某一API的Python类--又是鸭子类型!在深入正式的API之前,让
我们先来看看简单的例子
高流量的站点通常需要在一个负载均衡代理后面部署Django(参考第12章),这会导致一些小的复杂性,其中一个是每个请求
的远程IP(request.META["REMOTE_IP"])将为负载均衡者的IP,而不是真实的IP生成的请求,负载均衡通过设置特殊的头部
X-Forwared-For到真实的请求IP地址来处理这些
所以这里是一些中间件让代理后面运行的站点仍然可以在request.META["REMOTE_IP"]看到正确的IP地址:
如果它被安装了(参考下面的),每个请求的X-Forwarded-For值将被自动插入到request.META['REMOTE_ADDR'],很简单吧?
事实上,这个中间件作为Django的内建部分是一个足够常见的需要,它位于django.middleware.http,你在下面可以看到一
些更多关于它的内容
安装中间件
线性阅读的读者可能已经对此是老手了,如果你已经指定怎样激活中间件则前面的一些章节中的许多例子将很好的工作
尽管如此,为了完整性考虑--以及为了考虑那些已经把本书的页面撕破并搅乱它们并随机阅读它们的Julio Cortázar 迷们
让我们来看看它
为了激活中间件组件,把它添加到你的settings模块的MIDDLEWARE_CLASSES列表中,在MIDDLEWARE_CLASSES里,每个中间件
组件通过一个字符串来表示:完整的到中间件的类名的Python路径,例如,这里是通过django-admin.py startproject创建的
默认的MIDDLEWARE_CLASSES:
Django安装部需要任何中间件--例如MIDDLEWARE_CLASSES可以为空,如果你想这样的话--但是强烈建议你使用CommonMiddle
ware
顺序是有意义的,在请求和视图阶段,Django使用MIDDLEWARE_CLASSES给定的顺序申请中间件,而在应答和异常阶段,Djang
o使用相反的顺序申请中间件,即Django把MIDDLEWARE_CLASSES当作一种视图方法的"包装器":在请求时,它自顶向下申请这
个列表的中间件到视图,而在应答时它反序进行
中间件方法
既然我们知道了什么是中间件和怎样安装它,让我们来看看中间件类可能定义的所有可得到的方法
初始化:__init__(self)
如果中间件类定义了一个构造方法(即一个__init__方法),它应该不使用参数(除了标准的slef)
出于性能考虑,中间件类在长时间运行的服务器过程中只初始化一次,这意味着你不能指望每次请求运行时调用__init__,
只有在服务器启动时调用一次
中间件类可能也使用初始化时间来删除它们本身而不是被安装,如果初始化时触发django.exceptions.MiddlewareNotUsed异
常,则Django将从中间件堆栈删除该中间件,你可能使用这点来检查中间件类依赖的一些软件,或者服务器是否运行在调试
模式下,或者任何其它可能让你想禁止中间件的类型的环境
请求预处理:process_request(self, request)
该方法在请求被接收和URL被解析来决定运行哪个视图之前立即调用,它传递你可能想修改的HttpRequest对象
process_request()应该返回None或者HttpResponse对象,如果它返回None,Django将继续处理该请求,执行任何其它的中间
件然后是合适的视图
如果请求中间件返回HttpResponse对象,Django将不会再调用其它任何中间件(任何类型)或者合适的视图,它将返回该应答
视图预处理:process_view(self, request, view, args, kwargs)
该方法在请求中间件运行后和URL被解析到一个视图后和视图实际上被调用之前被调用
传递给该视图的参数为:
Argument Explanation
request HttpRequest对象
view Django将会调用来树立该请求的Python方法,它是实际上的方法对象本身,而不是方法名字符串
args 将被传递给视图的位置参数列表,不包括request参数(它一直是视图的第一个参数)
kwargs 将被传递给视图的关键字参数字典
像process_request()一样,process_view()应该返回None或者HttpResponse对象,如果它返回None,Django将继续处理请求
执行任何其它的视图中间件然后是合适的视图
如果视图中间件返回HttpResponse对象,Django将不会再调用其它任何中间件或者合适的视图,它将返回该应答
应答后处理:process_response(self, request, response)
该方法再视图方法已经调用和应答生成后调用,这是中间件修改应答输出的地方,输出压缩(见下)是应答中间件的一个显然
的应用
参数应该非常自明了--request是请求对象,response是从视图返回的应答对象
不像请求和视图中间件,它们可以返回None,process_response()必须返回一个HttpResponse对象,该应答可以是传递给该
方法的原始应答(可能被修改了)或者新的应答
异常后处理:process_exception(self, request, exception)
该方法只在出错并且视图触发不可捕获的异常时调用,不包括Http404异常,你可以使用这个钩子来发送错误通知,在一个日
志文件记录信息,或者甚至尝试自动恢复该错误
该方法的参数是我们一直处理的同样的request对象和视图方法触发的真正的Exception对象exception
process_exception()可能返回一个作为应答显示给浏览器的HttpResponse或者返回None来继续Django内建的异常处理
例子
Django自带一些中间件类--上面讨论了--它们是很好的例子,阅读它们的代码应该给你中间件的力量的好的感觉
你也可以在Django的wiki上找到一些社区贡献的例子:
http://code.djangoproject.com/wiki/ContributedMiddleware
内建的中间件
Django带有一些内建的中间件来处理常见的问题
认证支持中间件
中间件类:django.contrib.auth.middleware.AuthenticationMiddleware
运行认证支持,技术上,该中间件为每个进来的HttpRequest对象添加了表示当前登录的用户的request.user属性
参考第15章得到完整的细节
"Common"中间件
中间件类:django.middleware.common.CommonMiddleware
为完美主义者添加一些方便:
1,禁止访问在DISALLOWED_USER_AGENTS设置中的user agents,该设置应该是一个字符串列表
2,基于APPEND_SLASH和PREPEND_WWW设置执行URL重定向,如果APPEND_SLASH为True,缺少结尾的斜线的URLs将被重定向到相
同的结尾有斜线的URL,除非路径中最后的组件包含一个小数点,所以foo.com/bar被重定向到foo.com/bar/,但是foo.com/
bar/file.txt会无更改的传递
如果PREPEND_WWW为True,缺少开头的"www."的URLs将被重定向到相同的有开头的"www."的URL
这些选项都意味着规范化URLs,哲学是每个URL应该应该存在于一个并且只有一个地方,技术上URL foo.com/bar和foo.com/
bar/不同--搜索引擎索引器将把它们视为不同的URLs,所以规范化URLs是最佳实践
3,基于USE_ETAGS设置处理ETags,如果USE_ETAGS设置为True,Django将通过MD5-哈希页面内容为每个请求计算Etag,并且
如果合适的话它将注意发送未修改的应答
压缩中间件
中间件类:django.middleware.gzip.GZipMiddleware
如果被激活,该中间件将为理解gzip压缩的浏览器(所有的现代浏览器)自动压缩内容
这可以以处理时间为代价很大的减少web服务器消耗的带宽数量,我们通常选择速度而不是带宽,但是如果你想使用相反的一
边,只需激活该中间件
有条件的GET中间件
中间件类:django.middleware.http.ConditionalGetMiddleware
如果被激活,它提供有条件的GET操作支持,如果应答有一个ETag或者Last-Modified头部,并请求有If-None-Match或者If-
Modified-Since,应答将被304("未修改")应答替换
它也为所有请求删除任何应答到HEAD请求的内容并设置Date和Content-Length应答头部
倒转代理支持(X-Forwarede-For中间件)
中间件类:django.middleware.http.SetRemoteAddrFromForwaredFor
则是我们上面看到的例子,它基于request.META['HTTP_X_FORWARDED_FOR']设置request.META['REMOTE_ADDR'],如果前者
设置了的话,这当你位于将每个请求的REMOTE_ADDR设置为127.0.0.1的倒转代理后面时很有用
危险,Will Robinson!
它不验证HTTP_X_FORWARDED_FOR
如果你没有在自动设置HTTP_X_FORWARDED_FOR的倒转代理后面,则不要使用该中间件,任何人都可以欺骗HTTP_X_FORWARDED_
FOR的值,由于它基于HTTP_X_FORWARDED_FOR设置REMOTE_ADDR,这意味着任何人可以伪造他们的IP地址
只有当你可以绝对信任HTTP_X_FORWARDED_FOR的值时使用该中间件
Session支持中间件
中间件类:django.contrib.sessions.middleware.SessionMiddleware
允许session支持,参考第15章的细节
站点宽度缓存中间件
中间件类:django.middleware.cache.CacheMiddleware
如果被激活,每个Django驱动的页面将被缓存,这在第14章详细讨论了
事务中间件
中间件类:django.middleware.transaction.TransactionMiddleware
在请求/应答阶段绑定数据库COMMIT或者ROLLBACK,如果视图方法成功运行,则COMMIT完成,如果它以异常失败,则ROLLBACK
完成
在堆栈中该中间件的顺序很重要:运行在它外面的中间件模块以Django默认行为commit-on-save运行,运行在它里面(在堆栈
中它后面)的中间件模块将和视图方法处于同一事务控制
参考XXX来得到更多关于数据库事务的信息
"X-View"中间件
中间件类:django.middleware.doc.XViewMiddleware
对来自INTERNAL_IPS设置中定义的IP地址的HEAD请求发送自定义X-View HTTP头部,它被Django的自动文档系统使用
有时你需要对每个Django处理的请求运行一段代码,该代码可能需要在视图处理它之前修改请求,或者可能日志为了调试而
记录关于请求的信息等等
Django的中间件框架本质上是进入Django的请求/应答处理的钩子,它是全局改变Django的输入和输出的轻量的,低级的"插
件"系统
每个中间件组件负责一些专有的功能,如果你线性的阅读本书,则你已经看到中间件好几次了:
1,我们在第12章看到的所有俏皮的会话和用户工具可能由一些小块的中间件构成(更明确的,中间件使得你在视图中可以得
到request.session和request.user)
2,第12章讨论的站点宽度的缓存事实上只是一些中间件,如果视图的应答已经被缓存则它们是调用你的视图方法的捷径
3,第15章的flatpages,redirects和csrf这些贡献的apps都是通过使用中间件组件来施展它们的魔力
本章深入了解什么是中间件以及它怎样工作,并解释了你怎样写你自己的中间件
什么是中间件?
中间件事实上难以置信的简单,一个中间件组件是简单的遵守某一API的Python类--又是鸭子类型!在深入正式的API之前,让
我们先来看看简单的例子
高流量的站点通常需要在一个负载均衡代理后面部署Django(参考第12章),这会导致一些小的复杂性,其中一个是每个请求
的远程IP(request.META["REMOTE_IP"])将为负载均衡者的IP,而不是真实的IP生成的请求,负载均衡通过设置特殊的头部
X-Forwared-For到真实的请求IP地址来处理这些
所以这里是一些中间件让代理后面运行的站点仍然可以在request.META["REMOTE_IP"]看到正确的IP地址:
class SetRemoteAddrFromForwardedFor(object): def process_request(self, request): try: real_ip = request.META['HTTP_X_FORWARDED_FOR'] except KeyError: pass else: # HTTP_X_FORWARDED_FOR can be a comma-separated list of IPs. # Take just the first one. real_ip = real_ip.split(",")[0] request.META['REMOTE_ADDR'] = real_ip
如果它被安装了(参考下面的),每个请求的X-Forwarded-For值将被自动插入到request.META['REMOTE_ADDR'],很简单吧?
事实上,这个中间件作为Django的内建部分是一个足够常见的需要,它位于django.middleware.http,你在下面可以看到一
些更多关于它的内容
安装中间件
线性阅读的读者可能已经对此是老手了,如果你已经指定怎样激活中间件则前面的一些章节中的许多例子将很好的工作
尽管如此,为了完整性考虑--以及为了考虑那些已经把本书的页面撕破并搅乱它们并随机阅读它们的Julio Cortázar 迷们
让我们来看看它
为了激活中间件组件,把它添加到你的settings模块的MIDDLEWARE_CLASSES列表中,在MIDDLEWARE_CLASSES里,每个中间件
组件通过一个字符串来表示:完整的到中间件的类名的Python路径,例如,这里是通过django-admin.py startproject创建的
默认的MIDDLEWARE_CLASSES:
MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.middleware.doc.XViewMiddleware', )
Django安装部需要任何中间件--例如MIDDLEWARE_CLASSES可以为空,如果你想这样的话--但是强烈建议你使用CommonMiddle
ware
顺序是有意义的,在请求和视图阶段,Django使用MIDDLEWARE_CLASSES给定的顺序申请中间件,而在应答和异常阶段,Djang
o使用相反的顺序申请中间件,即Django把MIDDLEWARE_CLASSES当作一种视图方法的"包装器":在请求时,它自顶向下申请这
个列表的中间件到视图,而在应答时它反序进行
中间件方法
既然我们知道了什么是中间件和怎样安装它,让我们来看看中间件类可能定义的所有可得到的方法
初始化:__init__(self)
如果中间件类定义了一个构造方法(即一个__init__方法),它应该不使用参数(除了标准的slef)
出于性能考虑,中间件类在长时间运行的服务器过程中只初始化一次,这意味着你不能指望每次请求运行时调用__init__,
只有在服务器启动时调用一次
中间件类可能也使用初始化时间来删除它们本身而不是被安装,如果初始化时触发django.exceptions.MiddlewareNotUsed异
常,则Django将从中间件堆栈删除该中间件,你可能使用这点来检查中间件类依赖的一些软件,或者服务器是否运行在调试
模式下,或者任何其它可能让你想禁止中间件的类型的环境
请求预处理:process_request(self, request)
该方法在请求被接收和URL被解析来决定运行哪个视图之前立即调用,它传递你可能想修改的HttpRequest对象
process_request()应该返回None或者HttpResponse对象,如果它返回None,Django将继续处理该请求,执行任何其它的中间
件然后是合适的视图
如果请求中间件返回HttpResponse对象,Django将不会再调用其它任何中间件(任何类型)或者合适的视图,它将返回该应答
视图预处理:process_view(self, request, view, args, kwargs)
该方法在请求中间件运行后和URL被解析到一个视图后和视图实际上被调用之前被调用
传递给该视图的参数为:
Argument Explanation
request HttpRequest对象
view Django将会调用来树立该请求的Python方法,它是实际上的方法对象本身,而不是方法名字符串
args 将被传递给视图的位置参数列表,不包括request参数(它一直是视图的第一个参数)
kwargs 将被传递给视图的关键字参数字典
像process_request()一样,process_view()应该返回None或者HttpResponse对象,如果它返回None,Django将继续处理请求
执行任何其它的视图中间件然后是合适的视图
如果视图中间件返回HttpResponse对象,Django将不会再调用其它任何中间件或者合适的视图,它将返回该应答
应答后处理:process_response(self, request, response)
该方法再视图方法已经调用和应答生成后调用,这是中间件修改应答输出的地方,输出压缩(见下)是应答中间件的一个显然
的应用
参数应该非常自明了--request是请求对象,response是从视图返回的应答对象
不像请求和视图中间件,它们可以返回None,process_response()必须返回一个HttpResponse对象,该应答可以是传递给该
方法的原始应答(可能被修改了)或者新的应答
异常后处理:process_exception(self, request, exception)
该方法只在出错并且视图触发不可捕获的异常时调用,不包括Http404异常,你可以使用这个钩子来发送错误通知,在一个日
志文件记录信息,或者甚至尝试自动恢复该错误
该方法的参数是我们一直处理的同样的request对象和视图方法触发的真正的Exception对象exception
process_exception()可能返回一个作为应答显示给浏览器的HttpResponse或者返回None来继续Django内建的异常处理
例子
Django自带一些中间件类--上面讨论了--它们是很好的例子,阅读它们的代码应该给你中间件的力量的好的感觉
你也可以在Django的wiki上找到一些社区贡献的例子:
http://code.djangoproject.com/wiki/ContributedMiddleware
内建的中间件
Django带有一些内建的中间件来处理常见的问题
认证支持中间件
中间件类:django.contrib.auth.middleware.AuthenticationMiddleware
运行认证支持,技术上,该中间件为每个进来的HttpRequest对象添加了表示当前登录的用户的request.user属性
参考第15章得到完整的细节
"Common"中间件
中间件类:django.middleware.common.CommonMiddleware
为完美主义者添加一些方便:
1,禁止访问在DISALLOWED_USER_AGENTS设置中的user agents,该设置应该是一个字符串列表
2,基于APPEND_SLASH和PREPEND_WWW设置执行URL重定向,如果APPEND_SLASH为True,缺少结尾的斜线的URLs将被重定向到相
同的结尾有斜线的URL,除非路径中最后的组件包含一个小数点,所以foo.com/bar被重定向到foo.com/bar/,但是foo.com/
bar/file.txt会无更改的传递
如果PREPEND_WWW为True,缺少开头的"www."的URLs将被重定向到相同的有开头的"www."的URL
这些选项都意味着规范化URLs,哲学是每个URL应该应该存在于一个并且只有一个地方,技术上URL foo.com/bar和foo.com/
bar/不同--搜索引擎索引器将把它们视为不同的URLs,所以规范化URLs是最佳实践
3,基于USE_ETAGS设置处理ETags,如果USE_ETAGS设置为True,Django将通过MD5-哈希页面内容为每个请求计算Etag,并且
如果合适的话它将注意发送未修改的应答
压缩中间件
中间件类:django.middleware.gzip.GZipMiddleware
如果被激活,该中间件将为理解gzip压缩的浏览器(所有的现代浏览器)自动压缩内容
这可以以处理时间为代价很大的减少web服务器消耗的带宽数量,我们通常选择速度而不是带宽,但是如果你想使用相反的一
边,只需激活该中间件
有条件的GET中间件
中间件类:django.middleware.http.ConditionalGetMiddleware
如果被激活,它提供有条件的GET操作支持,如果应答有一个ETag或者Last-Modified头部,并请求有If-None-Match或者If-
Modified-Since,应答将被304("未修改")应答替换
它也为所有请求删除任何应答到HEAD请求的内容并设置Date和Content-Length应答头部
倒转代理支持(X-Forwarede-For中间件)
中间件类:django.middleware.http.SetRemoteAddrFromForwaredFor
则是我们上面看到的例子,它基于request.META['HTTP_X_FORWARDED_FOR']设置request.META['REMOTE_ADDR'],如果前者
设置了的话,这当你位于将每个请求的REMOTE_ADDR设置为127.0.0.1的倒转代理后面时很有用
危险,Will Robinson!
它不验证HTTP_X_FORWARDED_FOR
如果你没有在自动设置HTTP_X_FORWARDED_FOR的倒转代理后面,则不要使用该中间件,任何人都可以欺骗HTTP_X_FORWARDED_
FOR的值,由于它基于HTTP_X_FORWARDED_FOR设置REMOTE_ADDR,这意味着任何人可以伪造他们的IP地址
只有当你可以绝对信任HTTP_X_FORWARDED_FOR的值时使用该中间件
Session支持中间件
中间件类:django.contrib.sessions.middleware.SessionMiddleware
允许session支持,参考第15章的细节
站点宽度缓存中间件
中间件类:django.middleware.cache.CacheMiddleware
如果被激活,每个Django驱动的页面将被缓存,这在第14章详细讨论了
事务中间件
中间件类:django.middleware.transaction.TransactionMiddleware
在请求/应答阶段绑定数据库COMMIT或者ROLLBACK,如果视图方法成功运行,则COMMIT完成,如果它以异常失败,则ROLLBACK
完成
在堆栈中该中间件的顺序很重要:运行在它外面的中间件模块以Django默认行为commit-on-save运行,运行在它里面(在堆栈
中它后面)的中间件模块将和视图方法处于同一事务控制
参考XXX来得到更多关于数据库事务的信息
"X-View"中间件
中间件类:django.middleware.doc.XViewMiddleware
对来自INTERNAL_IPS设置中定义的IP地址的HEAD请求发送自定义X-View HTTP头部,它被Django的自动文档系统使用
评论 共 0 条 请登录后发表评论