在有些场合,须要对Django处理的每一个request都执行某段代码。 这类代码多是在view处理以前修改传入的request,或者记录日志信息以便于调试,等等。正则表达式
这类功能能够用Django的中间件框架来实现,该框架由切入到Django的request/response处理过程当中的钩子集合组成。 这个轻量级低层次的plug-in系统,能用于全面的修改Django的输入和输出。算法
每一个中间件组件都用于某个特定的功能。 若是你是顺着这本书读下来的话,你应该已经屡次见到“中间件”了数据库
第12章中全部的session和user工具都籍由一小簇中间件实现(例如,由中间件设定view中可见的 request.session
和 request.user
)。django
第13章讨论的站点范围cache实际上也是由一个中间件实现,一旦该中间件发现与view相应的response已在缓存中,就再也不调用对应的view函数。浏览器
第14章所介绍的 flatpages
, redirects
, 和 csrf
等应用也都是经过中间件组件来完成其魔法般的功能。缓存
这一章将深刻到中间件及其工做机制中,并阐述如何自行编写中间件。服务器
咱们从一个简单的例子开始。session
高流量的站点一般须要将Django部署在负载平衡proxy(参见第20章)以后。 这种方式将带来一些复杂性,其一就是每一个request中的远程IP地址(request.META["REMOTE_IP"]
)将指向该负载平衡proxy,而不是发起这个request的实际IP。 负载平衡proxy处理这个问题的方法在特殊的 X-Forwarded-For
中设置实际发起请求的IP。app
所以,须要一个小小的中间件来确保运行在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
(Note: Although the HTTP header is called X-Forwarded-For
, Django makes it available as request.META['HTTP_X_FORWARDED_FOR']
. With the exception of content-length
and content-type
, any HTTP headers in the request are converted to request.META
keys by converting all characters to uppercase, replacing any hyphens with underscores and adding an HTTP_
prefix to the name.)
一旦安装了该中间件(参见下一节),每一个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_CLASSES
能够为空。
这里中间件出现的顺序很是重要。 在request和view的处理阶段,Django按照 MIDDLEWARE_CLASSES
中出现的顺序来应用中间件,而在response和异常处理阶段,Django则按逆序来调用它们。 也就是说,Django将 MIDDLEWARE_CLASSES
视为view函数外层的顺序包装子: 在request阶段按顺序从上到下穿过,而在response则反过来。
如今,咱们已经知道什么是中间件和怎么安装它,下面将介绍中间件类中能够定义的全部方法。
在中间件类中, __init__()
方法用于执行系统范围的设置。
出于性能的考虑,每一个已启用的中间件在每一个服务器进程中只初始化 一 次。 也就是说 __init__()
仅在服务进程启动的时候调用,而在针对单个request处理时并不执行。
对一个middleware而言,定义 __init__()
方法的一般缘由是检查自身的必要性。 若是 __init__()
抛出异常 django.core.exceptions.MiddlewareNotUsed
,则Django将从middleware栈中移出该middleware。 能够用这个机制来检查middleware依赖的软件是否存在、服务是否运行于调试模式、以及任何其它环境因素。
在中间件中定义 __init__()
方法时,除了标准的 self
参数以外,不该定义任何其它参数。
这个方法的调用时机在Django接收到request以后,但仍未解析URL以肯定应当运行的view以前。 Django向它传入相应的 HttpRequest
对象,以便在方法中修改。
process_request()
应当返回 None
或 HttpResponse
对象.
若是返回 None
, Django将继续处理这个request,执行后续的中间件, 而后调用相应的view.
若是返回 HttpResponse
对象, Django 将再也不执行 任何 其它的中间件(而无视其种类)以及相应的view。 Django将当即返回该 HttpResponse
.
这个方法的调用时机在Django执行完request预处理函数并肯定待执行的view以后,但在view函数实际执行以前。
表15-1列出了传入到这个View预处理函数的参数。
参数 | 说明 |
---|---|
request |
The HttpRequest object. |
view |
The Python function that Django will call to handle this request. This is the actual function object itself, not the name of the function as a string. |
args |
将传入view的位置参数列表,但不包括
|
kwargs |
将传入view的关键字参数字典. |
Just like process_request()
, process_view()
should return either None
or an HttpResponse
object.
If it returns None
, Django will continue processing this request, executing any other middleware and then the appropriate view.
If it returns an HttpResponse
object, Django won’t bother calling any other middleware (of any type) or the appropriate view. Django will immediately return that HttpResponse
.
这个方法的调用时机在Django执行view函数并生成response以后。 Here, the processor can modify the content of a response. One obvious use case is content compression, such as gzipping of the request’s HTML.
这个方法的参数至关直观: request
是request对象,而 response
则是从view中返回的response对象。 request
is the request object, and response
is the response object returned from the view.
不一样可能返回 None
的request和view预处理函数, process_response()
必须 返回 HttpResponse
对象. 这个response对象能够是传入函数的那一个原始对象(一般已被修改),也能够是全新生成的。 That response could be the original one passed into the function (possibly modified) or a brand-new one.
这个方法只有在request处理过程当中出了问题而且view函数抛出了一个未捕获的异常时才会被调用。 这个钩子能够用来发送错误通知,将现场相关信息输出到日志文件, 或者甚至尝试从错误中自动恢复。
这个函数的参数除了一向的 request
对象以外,还包括view函数抛出的实际的异常对象 exception
。
process_exception()
应当返回 None 或 HttpResponse 对象.
若是返回 None
, Django将用框架内置的异常处理机制继续处理相应request。
若是返回 HttpResponse
对象, Django 将使用该response对象,而短路框架内置的异常处理机制。
备注
Django自带了至关数量的中间件类(将在随后章节介绍),它们都是至关好的范例。 阅读这些代码将使你对中间件的强大有一个很好的认识。
在Djangos wiki上也能够找到大量的社区贡献的中间件范例: http://code.djangoproject.com/wiki/ContributedMiddlewarehttp://code.djangoproject.com/wiki/ContributedMiddleware
Django自带若干内置中间件以处理常见问题,将从下一节开始讨论。
中间件类: django.contrib.auth.middleware.AuthenticationMiddleware
. django.contrib.auth.middleware.AuthenticationMiddleware
.
这个中间件激活认证支持功能. 它在每一个传入的 HttpRequest
对象中添加表明当前登陆用户的 request.user
属性。 It adds the request.user
attribute, representing the currently logged-in user, to every incoming HttpRequest
object.
完整的细节请参见第12章。
Middleware class: 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()
的返回值)。依据 ``APPEND_SLASH`` 和 ``PREPEND_WWW`` 的设置执行URL重写 :若是
APPEND_SLASH
为True
, 那些尾部没有斜杠的URL将被重定向到添加了斜杠的相应URL,除非path的最末组成部分包含点号。 所以,foo.com/bar
会被重定向到foo.com/bar/
, 可是foo.com/bar/file.txt
将以不变形式经过。若是
PREPEND_WWW
为 True , 那些缺乏先导www.的URLs将会被重定向到含有先导www.的相应URL上。 will be redirected to the same URL with a leading www..这两个选项都是为了规范化URL。 其后的哲学是每一个URL都应且只应当存在于一处。 技术上来讲,URL
example.com/bar
与example.com/bar/
及www.example.com/bar/
都互不相同。依据 ``USE_ETAGS`` 的设置处理Etag : ETags 是HTTP级别上按条件缓存页面的优化机制。 若是
USE_ETAGS
为True
,Django针对每一个请求以MD5算法处理页面内容,从而获得Etag, 在此基础上,Django将在适当情形下处理并返回Not Modified
回应(译注:请注意,还有一个条件化的
GET
中间件, 处理Etags并干得更多,下面立刻就会说起。
中间件类 django.middleware.gzip.GZipMiddleware
.
这个中间件自动为能处理gzip压缩(包括全部的现代浏览器)的浏览器自动压缩返回]内容。 这将极大地减小Web服务器所耗用的带宽。 代价是压缩页面须要一些额外的处理时间。
相对于带宽,人们通常更青睐于速度,可是若是你的情形正好相反,尽可启用这个中间件。
Middleware class: django.middleware.http.ConditionalGetMiddleware
.
这个中间件对条件化 GET
操做提供支持。 若是response头中包括 Last-Modified
或 ETag
域,而且request头中包含 If-None-Match
或 If-Modified-Since
域,且二者一致,则该response将被response 304(Not modified)取代。 对 ETag
的支持依赖于 USE_ETAGS
配置及事先在response头中设置 ETag
域。稍前所讨论的通用中间件可用于设置response中的 ETag
域。 As discussed above, the ETag
header is set by the Common middleware.
此外,它也将删除处理 HEAD
request时所生成的response中的任何内容,并在全部request的response头中设置 Date
和 Content-Length
域。
Middleware class: django.middleware.http.SetRemoteAddrFromForwardedFor
.
这是咱们在 什么是中间件 这一节中所举的例子。 在 request.META['HTTP_X_FORWARDED_FOR']
存在的前提下,它根据其值来设置 request.META['REMOTE_ADDR']
。在站点位于某个反向代理以后的、每一个request的 REMOTE_ADDR
都被指向 127.0.0.1
的情形下,这一功能将很是有用。 It sets request.META['REMOTE_ADDR']
based on request.META['HTTP_X_FORWARDED_FOR']
, if the latter is set. This is useful if you’re sitting behind a reverse proxy that causes each request’s REMOTE_ADDR
to be set to 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
值得时候才可以使用这个中间件。
Middleware class: django.contrib.sessions.middleware.SessionMiddleware
.
这个中间件激活会话支持功能. 细节请参见第12章。 See Chapter 14 for details.
Middleware classes: django.middleware.cache.UpdateCacheMiddleware
and django.middleware.cache.FetchFromCacheMiddleware
.
这些中间件互相配合以缓存每一个基于Django的页面。 已在第13章中详细讨论。
Middleware class: django.middleware.transaction.TransactionMiddleware
.
这个中间件将数据库的 COMMIT
或 ROLLBACK
绑定到request/response处理阶段。 若是view函数成功执行,则发出 COMMIT
指令。 若是view函数抛出异常,则发出 ROLLBACK
指令。
这个中间件在栈中的顺序很是重要。 其外层的中间件模块运行在Django缺省的 保存-提交 行为模式下。 而其内层中间件(在栈中的其后位置出现)将置于与view函数一致的事务机制的控制下。
关于数据库事务处理的更多信息,请参见附录C。