Django中间件

在有些场合,需要对Django处理的每个request都执行某段代码。这类代码可能是在view处理之前修改传入的request,或者记录日志信息以便于调试,等等。

这类功能可以用Django的中间件框架来实现,该框架由切入到Djangorequest/response处理过程中的钩子集合组成。这个轻量级低层次的plug-in系统,能用于全面的修改Django的输入和输出。

每个中间件组件都用于某个特定的功能。如果顺序阅读这本书(谨对后现代主义者表示抱歉),你可能已经多次看到中间件了:

§ Django会话、用户和注册中所有的sessionuser工具都籍由一小簇中间件实现(例如,由中间件设定view中可见的request.sessionrequest.user)

§ Django缓存机制讨论的站点范围cache实际上也是由一个中间件实现,一旦该中间件发现与view相应的response已在缓存中,就不再调用对应的view函数。

§ Django集成的子框架所介绍的flatpages,redirects,csrf等应用也都是通过中间件组件来完成其魔法般的功能。

这一章将深入到中间件及其工作机制中,并阐述如何自行编写中间件。

什么是中间件

中间件组件是遵循特定API规则的简单Python类。在深入到该API规则的正式细节之前,先看一下下面这个非常简单的例子。

高流量的站点通常需要将Django部署在负载平衡proxy(参见第20)之后。这种方式将带来一些复杂性,其一就是每个request中的远程IP地址(request.META["REMOTE_IP"])将指向该负载平衡proxy,而不是发起这个request的实际IP。负载平衡proxy处理这个问题的方法在特殊的X-Forwarded-For中设置实际发起请求的IP

因此,需要一个小小的中间件来确保运行在proxy之后的站点也能够在request.META["REMOTE_ADDR"]中得到正确的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

一旦安装了该中间件(参见下一节),每个request中的X-Forwarded-For值都会被自动插入到request.META['REMOTE_ADDR']中。这样,Django应用就不需要关心自己是否位于负载平衡proxy之后;简单读取request.META['REMOTE_ADDR']的方式在是否有proxy的情形下都将正常工作。

实际上,为针对这个非常常见的情形,Django已将该中间件内置。它位于django.middleware.http,下一节将给出这个中间件相关的更多细节。

安装中间件

如果按顺序阅读本书,应当已经看到涉及到中间件安装的多个示例,因为前面章节的许多例子都需要某些特定的中间件。出于完整性考虑,下面介绍如何安装中间件。

要启用一个中间件,只需将其添加到配置模块的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可以为空。但我们建议启用CommonMiddleware,稍后做出解释。

这里中间件出现的顺序非常重要。在requestview的处理阶段,Django按照MIDDLEWARE_CLASSES中出现的顺序来应用中间件,而在response和异常处理阶段,Django则按逆序来调用它们。也就是说,DjangoMIDDLEWARE_CLASSES视为view函数外层的顺序包装子:在request阶段按顺序从上到下穿过,而在response则反过来。

中间件方法

现在,我们已经知道什么是中间件和怎么安装它,下面将介绍中间件类中可以定义的所有方法。

Initializer: __init__(self)

在中间件类中,__init__()方法用于执行系统范围的设置。

出于性能的考虑,每个已启用的中间件在每个服务器进程中只初始化次。也就是说__init__()仅在服务进程启动的时候调用,而在针对单个request处理时并不执行。

对一个middleware而言,定义__init__()方法的通常原因是检查自身的必要性。如果__init__()抛出异常django.core.exceptions.MiddlewareNotUsed,Django将从middleware栈中移出该middleware。可以用这个机制来检查middleware依赖的软件是否存在、服务是否运行于调试模式、以及任何其它环境因素。

在中间件中定义__init__()方法时,除了标准的self参数之外,不应定义任何其它参数。

Request预处理函数: process_request(self, request)

这个方法的调用时机在Django接收到request之后,但仍未解析URL以确定应当运行的view之前。Django向它传入相应的HttpRequest对象,以便在方法中修改。

process_request()应当返回NoneHttpResponse对象.

§ 如果返回None, Django将继续处理这个request,执行后续的中间件,然后调用相应的view.

§ 如果返回HttpResponse对象, Django将不再执行任何其它的中间件(而无视其种类)以及相应的view Django将立即返回该HttpResponse.

View预处理函数: process_view(self, request, view, args, kwargs)

这个方法的调用时机在Django执行完request预处理函数并确定待执行的view之后,但在view函数实际执行之前。

15-1列出了传入到这个View预处理函数的参数。

表 15-1. 传入process_view()的参数

参数

说明

request

HttpRequest对象 .

view

Django将调用的处理request的python函数. 这是实际的函数对象本身, 而不是字符串表述的函数名。

args

将传入view的位置参数列表,但不包括 request参数(它通常是传 入view的第一个参数)

kwargs

将传入view的关键字参数字典.

如同process_request(),process_view()应当返回NoneHttpResponse对象。

§ 如果返回None, Django将继续处理这个request,执行后续的中间件,然后调用相应的view.

§ 如果返回HttpResponse对象, Django将不再执行任何其它的中间件(不论种类)以及相应的view. Django将立即返回该HttpResponse.

Response后处理函数: process_response(self, request, response)

这个方法的调用时机在Django执行view函数并生成response之后。这里,该处理器就能修改response的内容;一个常见的用途是内容压缩,如gzip所请求的HTML页面。

这个方法的参数相当直观:requestrequest对象,而response则是从view中返回的response对象。

不同可能返回Nonerequestview预处理函数,process_response()必须返回HttpResponse对象.这个response对象可以是传入函数的那一个原始对象(通常已被修改),也可以是全新生成的。

Exception后处理函数: process_exception(self, request, exception)

这个方法只有在request处理过程中出了问题并且view函数抛出了一个未捕获的异常时才会被调用。这个钩子可以用来发送错误通知,将现场相关信息输出到日志文件,或者甚至尝试从错误中自动恢复。

这个函数的参数除了一贯的request对象之外,还包括view函数抛出的实际的异常对象exception

process_exception()应当返回 None HttpResponse 对象.

§ 如果返回None, Django将用框架内置的异常处理机制继续处理相应request

§ 如果返回HttpResponse对象, Django将使用该response对象,而短路框架内置的异常处理机制。

备注

Django自带了相当数量的中间件类(将在随后章节介绍),它们都是相当好的范例。阅读这些代码将使你对中间件的强大有一个很好的认识。

Djangos wiki上也可以找到大量的社区贡献的中间件范例:http://code.djangoproject.com/wiki/ContributedMiddleware

内置的中间件

Django自带若干内置中间件以处理常见问题,将从下一节开始讨论。

认证支持中间件

中间件类:django.contrib.auth.middleware.AuthenticationMiddleware.

这个中间件激活认证支持功能.它在每个传入的HttpRequest对象中添加代表当前登录用户的request.user属性。

完整的细节请参见Django会话、用户和注册

通用中间件

中间件类:django.middleware.common.CommonMiddleware.

这个中间件为完美主义者提供了一些便利:

禁止 ``DISALLOWED_USER_AGENTS``列表中所设置的user agent访问:一旦提供,这一列表应当由已编译的正则表达式对象组成,这些对象用于匹配传入的request请求头中的user-agent域。下面这个例子来自某个配置文件片段:

import re

DISALLOWED_USER_AGENTS = (

re.compile(r'^OmniExplorer_Bot'),

re.compile(r'^Googlebot')

)

请注意import re,因为DISALLOWED_USER_AGENTS要求其值为已编译的正则表达式(也就是re.compile()的返回值)。配置文件是常规的python文件,所以在其中包括Pythonimport语句不会有任何问题。

依据 ``APPEND_SLASH`` ``PREPEND_WWW``的设置执行URL重写:如果APPEND_SLASHTrue,那些尾部没有斜杠的URL将被重定向到添加了斜杠的相应URL,除非path的最末组成部分包含点号。因此,foo.com/bar会被重定向到foo.com/bar/,但是foo.com/bar/file.txt将以不变形式通过。

如果PREPEND_WWW True ,那些缺少先导www.URLs将会被重定向到含有先导www.的相应URL上。

这两个选项都是为了规范化URL。其后的哲学是每个URL都应且只应当存在于一处。技术上来说,URLexample.com/barexample.com/bar/www.example.com/bar/都互不相同。搜索引擎编目程序将把它们视为不同的URL,这将不利于该站点的搜索引擎排名,因此这里的最佳实践是将URL规范化。

依据 ``USE_ETAGS``的设置处理Etag:ETags HTTP级别上按条件缓存页面的优化机制。如果USE_ETAGSTrueDjango针对每个请求以MD5算法处理页面内容,从而得到Etag,在此基础上,Django将在适当情形下处理并返回Not Modified回应(译注:或者设置response头中的Etag)

请注意,还有一个条件化的GET中间件,处理Etags并干得更多,下面马上就会提及。

压缩中间件

中间件类:django.middleware.gzip.GZipMiddleware.

这个中间件自动为能处理gzip压缩(包括所有的现代浏览器)的浏览器自动压缩返回]内容。这将极大地减少Web服务器所耗用的带宽。代价是压缩页面需要一些额外的处理时间。

相对于带宽,人们一般更青睐于速度,但是如果你的情形正好相反,尽可启用这个中间件。

条件化的GET中间件

中间件类:django.middleware.http.ConditionalGetMiddleware.

这个中间件对条件化GET操作提供支持。如果response头中包括Last-ModifiedETag域,并且request头中包含If-None-MatchIf-Modified-Since域,且两者一致,则该response将被response 304(Not modified)取代。对ETag的支持依赖于USE_ETAGS配置及事先在response头中设置ETag域。稍前所讨论的通用中间件可用于设置response中的ETag域。

此外,它也将删除处理HEADrequest时所生成的response中的任何内容,并在所有requestresponse头中设置DateContent-Length域。

反向代理支持 (X-Forwarded-For中间件)

中间件类:django.middleware.http.SetRemoteAddrFromForwardedFor.

这是我们在什么是中间件这一节中所举的例子。request.META['HTTP_X_FORWARDED_FOR']存在的前提下,它根据其值来设置request.META['REMOTE_ADDR']。在站点位于某个反向代理之后的、每个requestREMOTE_ADDR都被指向127.0.0.1的情形下,这一功能将非常有用。

红色警告!

这个middleware验证HTTP_X_FORWARDED_FOR的合法性。

如果站点并不位于自动设置HTTP_X_FORWARDED_FOR的反向代理之后,请不要使用这个中间件。否则,因为任何人都能够伪造HTTP_X_FORWARDED_FOR值,而REMOTE_ADDR又是依据HTTP_X_FORWARDED_FOR来设置,这就意味着任何人都能够伪造IP地址。

只有当能够绝对信任HTTP_X_FORWARDED_FOR值得时候才能够使用这个中间件。

会话支持中间件

中间件类:django.contrib.sessions.middleware.SessionMiddleware.

这个中间件激活会话支持功能.细节请参见Django会话、用户和注册

站点缓存中间件

中间件类:django.middleware.cache.CacheMiddleware.

这个中间件缓存Django处理的每个页面。已在Django缓存机制中详细讨论。

事务处理中间件

中间件类:django.middleware.transaction.TransactionMiddleware.

这个中间件将数据库的COMMITROLLBACK绑定到request/response处理阶段。如果view函数成功执行,则发出COMMIT指令。如果view函数抛出异常,则发出ROLLBACK指令。

这个中间件在栈中的顺序非常重要。其外层的中间件模块运行在Django缺省的保存-提交行为模式下。而其内层中间件(在栈中的其后位置出现)将置于与view函数一致的事务机制的控制下。


X-View 中间件

中间件类:django.middleware.doc.XViewMiddleware.

这个中间件将对来自INTERNAL_IPS所设置的内部IPHEAD请求发送定制的X-ViewHTTP头。Django的自动文档系统使用了这个中间件。


转载请注明文章出处:http://blog.csdn.net/wolaiye320/article/details/52035451

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值