The Django Book

Chapter 15: Middleware

第十五章 中间件

On occasion, youll need to run a piece of code on each and every request that Django handles. This code might need to modify the request before the view handles it, it might need to log information about the request for debugging purposes, and so forth.


You can do this with Djangos middleware framework, which is a set of hooks into Djangos request/response processing. Its a light, low-level plug-in system capable of globally altering both Djangos input and output.


Each middleware component is responsible for doing some specific function. If youre reading this book linearly (sorry, postmodernists), youve seen middleware a number of times already:


  • All of the session and user tools that we looked at in Chapter 12 are made possible by a few small pieces of middleware (more specifically, the middleware makes request.session and request.user available to you in views).

  • 第12章中所有的session和user工具都籍由一小簇中间件实现(例如,由中间件设定view中可见的 request.sessionrequest.user )。

  • The sitewide cache discussed in Chapter 13 is actually just a piece of middleware that bypasses the call to your view function if the response for that view has already been cached.

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

  • The flatpages , redirects , and csrf contributed applications from Chapter 14 all do their magic through middleware components.

  • 第14章所介绍的 flatpages , redirects , 和 csrf 等应用也都是通过中间件组件来完成其魔法般的功能。

This chapter dives deeper into exactly what middleware is and how it works, and explains how you can write your own middleware.


Whats Middleware?


A middleware component is simply a Python class that conforms to a certain API. Before diving into the formal aspects of what that API is, lets look at a very simple example.


High-traffic sites often need to deploy Django behind a load-balancing proxy (see Chapter 20). This can cause a few small complications, one of which is that every requests remote IP (request.META["REMOTE_IP"] ) will be that of the load balancer, not the actual IP making the request. Load balancers deal with this by setting a special header, X-Forwarded-For , to the actual requesting IP address.

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

So heres a small bit of middleware that lets sites running behind a proxy still see the correct IP address in request.META["REMOTE_ADDR"] :

因此,需要一个小小的中间件来确保运行在proxy之后的站点也能够在 request.META["REMOTE_ADDR"] 中得到正确的IP地址:

class SetRemoteAddrFromForwardedFor(object):
    def process_request(self, request):
            real_ip = request.META['HTTP_X_FORWARDED_FOR']
        except KeyError:
            # 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

If this is installed (see the next section), every requests X-Forwarded-For value will be automatically inserted into request.META['REMOTE_ADDR'] . This means your Django applications dont need to be concerned with whether theyre behind a load-balancing proxy or not; they can simply access request.META['REMOTE_ADDR'] , and that will work whether or not a proxy is being used.

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

In fact, this is a common enough need that this piece of middleware is a built-in part of Django. It lives in django.middleware.http , and you can read a bit more about it in the next section.

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

Middleware Installation


If youve read this book straight through, youve already seen a number of examples of middleware installation; many of the examples in previous chapters have required certain middleware. For completeness, heres how to install middleware.


To activate a middleware component, add it to the MIDDLEWARE_CLASSES tuple in your settings module. In MIDDLEWARE_CLASSES , each middleware component is represented by a string: the full Python path to the middlewares class name. For example, heres the default MIDDLEWARE_CLASSES created by startproject :

要启用一个中间件,只需将其添加到配置模块的 MIDDLEWARE_CLASSES 元组中。在 MIDDLEWARE_CLASSES 中,中间件组件用字符串表示:指向中间件类名的完整Python路径。例如,下面是 startproject 创建的缺省 MIDDLEWARE_CLASSES :


A Django installation doesnt require any middleware MIDDLEWARE_CLASSES can be empty, if youd like but we recommend that you activate CommonMiddleware , which we explain shortly.

Django项目的安装并不强制要求任何中间件,如果你愿意, MIDDLEWARE_CLASSES 可以为空。但我们建议启用 CommonMiddleware ,稍后做出解释。

The order is significant. On the request and view phases, Django applies middleware in the order given in MIDDLEWARE_CLASSES , and on the response and exception phases, Django applies middleware in reverse order. That is, Django treats MIDDLEWARE_CLASSES as a sort of wrapper around the view function: on the request it walks down the list to the view, and on the response it walks back up. See the section How Django Processes a Request: Complete Details in Chapter 3 for a review of the phases.

这里中间件出现的顺序非常重要。在request和view的处理阶段,Django按照 MIDDLEWARE_CLASSES 中出现的顺序来应用中间件,而在response和异常处理阶段,Django则按逆序来调用它们。也就是说,Django将 MIDDLEWARE_CLASSES 视为view函数外层的顺序包装子:在request阶段按顺序从上到下穿过,而在response则反过来。关于Django处理阶段的详细信息,请参见第三章”Django怎么处理一个请求: 完整细节”这一节。

Middleware Methods


Now that you know what middleware is and how to install it, lets take a look at all the available methods that middleware classes can define.


Initializer: __init__(self)

Initializer: __init__(self)

Use __init__() to perform systemwide setup for a given middleware class.

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

For performance reasons, each activated middleware class is instantiated only once per server process. This means that __init__() is called only once at server startup not for individual requests.

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

A common reason to implement an __init__() method is to check whether the middleware is indeed needed. If __init__() raises django.core.exceptions.MiddlewareNotUsed , then Django will remove the middleware from the middleware stack. You might use this feature to check for some piece of software that the middleware class requires, or check whether the server is running debug mode, or any other such environment situation.

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

If a middleware class defines an __init__() method, the method should take no arguments beyond the standard self .

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

Request Preprocessor: process_request(self, request)

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

This method gets called as soon as the request has been received before Django has parsed the URL to determine which view to run. It gets passed the HttpRequest object, which you may modify at will.

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

process_request() should return either None or an HttpResponse object.

process_request() 应当返回 NoneHttpResponse 对象.

  • If it returns None , Django will continue processing this request, executing any other middleware and then the appropriate view.

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

  • If it returns an HttpResponse object, Django wont bother calling any other middleware (of any type) or the appropriate view. Django will immediately return that HttpResponse .

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

View Preprocessor: process_view(self, request, view, args, kwargs)

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

This method gets called after the request preprocessor is called and Django has determined which view to execute, but before that view has actually been executed.


The arguments passed to this view are shown in Table 15-1.


Table 15-1. Arguments Passed to process_view()
Argument Explanation
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 The list of positional arguments that will be passed to the view, not including the request argument (which is always the first argument to a view).
kwargs The dictionary of keyword arguments that will be passed to the view.

SoSjGG <a href=”“>yiqzndzralaf</a>, [url=]gjemutbubtxu[/url], [link=]zgdgecnxtllq[/link],

Just like process_request() , process_view() should return either None or an HttpResponse object.

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

  • If it returns None , Django will continue processing this request, executing any other middleware and then the appropriate view.

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

  • If it returns an HttpResponse object, Django wont bother calling any other middleware (of any type) or the appropriate view. Django will immediately return that HttpResponse .

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

Response Postprocessor: process_response(self, request, response)

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

This method gets called after the view function is called and the response is generated. Here, the processor can modify the content of a response; one obvious use case is content compression, such as gzipping of the requests HTML.


The parameters should be pretty self-explanatory: request is the request object, and response is the response object returned from the view.

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

Unlike the request and view preprocessors, which may return None , process_response() must return an HttpResponse object. That response could be the original one passed into the function (possibly modified) or a brand-new one.

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

Exception Postprocessor: process_exception(self, request, exception)

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

This method gets called only if something goes wrong and a view raises an uncaught exception. You can use this hook to send error notifications, dump postmortem information to a log, or even try to recover from the error automatically.

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

The parameters to this function are the same request object weve been dealing with all along, and exception , which is the actual Exception object raised by the view function.

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

process_exception() should return a either None or an HttpResponse object.

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

  • If it returns None , Django will continue processing this request with the frameworks built-in exception handling.

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

  • If it returns an HttpResponse object, Django will use that response instead of the frameworks built-in exception handling.

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



Django ships with a number of middleware classes (discussed in the following section) that make good examples. Reading the code for them should give you a good feel for the power of middleware.


You can also find a number of community-contributed examples on Djangos wiki:

在Djangos wiki上也可以找到大量的社区贡献的中间件范例:

Built-in Middleware


Django comes with some built-in middleware to deal with common problems, which we discuss in the sections that follow.


Authentication Support Middleware


Middleware class: django.contrib.auth.middleware.AuthenticationMiddleware .

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

This middleware enables authentication support. It adds the request.user attribute, representing the currently logged-in user, to every incoming HttpRequest object.

wSOx5R <a href=”“>hztnqpaboavm</a>, [url=]vmvmmjrsqxic[/url], [link=]sngrfxoajwdo[/link],

See Chapter 12 for complete details.


Common Middleware

SP3df8 <a href=”“>bfqhoouwrdmm</a>, [url=]hzujzeyighho[/url], [link=]mbrgpoykhljx[/link],

Middleware class: django.middleware.common.CommonMiddleware .

mBh1AN <a href=”“>vmlyatgebrhc</a>, [url=]dvatqsqhoxsu[/url], [link=]zuchupmuwhub[/link],

This middleware adds a few conveniences for perfectionists:


Forbids access to user agents in the ``DISALLOWED_USER_AGENTS`` setting : If provided, this setting should be a list of compiled regular expression objects that are matched against the user-agent header for each incoming request. Heres an example snippet from a settings file:

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

import re


Note the import re , because DISALLOWED_USER_AGENTS requires its values to be compiled regexes (i.e., the output of re.compile() ). The settings file is regular python, so its perfectly OK to include Python import statements in it.

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

Performs URL rewriting based on the ``APPEND_SLASH`` and ``PREPEND_WWW`` settings : If APPEND_SLASH is True , URLs that lack a trailing slash will be redirected to the same URL with a trailing slash, unless the last component in the path contains a period. So is redirected to , but is passed through unchanged.

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

If PREPEND_WWW is True , URLs that lack a leading www. will be redirected to the same URL with a leading www..

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

Both of these options are meant to normalize URLs. The philosophy is that each URL should exist in one and only one place. Technically the URL is distinct from , which in turn is distinct from . A search-engine indexer would treat these as separate URLs, which is detrimental to your sites search-engine rankings, so its a best practice to normalize URLs.

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

Handles ETags based on the ``USE_ETAGS`` setting : ETags are an HTTP-level optimization for caching pages conditionally. If USE_ETAGS is set to True , Django will calculate an ETag for each request by MD5-hashing the page content, and it will take care of sending Not Modified responses, if appropriate.

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

Note there is also a conditional GET middleware, covered shortly, which handles ETags and does a bit more.

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

Compression Middleware


Middleware class: django.middleware.gzip.GZipMiddleware .

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

This middleware automatically compresses content for browsers that understand gzip compression (all modern browsers). This can greatly reduce the amount of bandwidth a Web server consumes. The tradeoff is that it takes a bit of processing time to compress pages.


We usually prefer speed over bandwidth, but if you prefer the reverse, just enable this middleware.


Conditional GET Middleware


Middleware class: django.middleware.http.ConditionalGetMiddleware .

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

This middleware provides support for conditional GET operations. If the response has an Last-Modified or ETag or header, and the request has If-None-Match or If-Modified-Since , the response is replaced by an 304 (Not modified) response. ETag support depends on on the USE_ETAGS setting and expects the ETag response header to already be set. As discussed above, the ETag header is set by the Common middleware.

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

It also removes the content from any response to a HEAD request and sets the Date and Content-Length response headers for all requests.

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

Reverse Proxy Support (X-Forwarded-For Middleware)

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

Middleware class: django.middleware.http.SetRemoteAddrFromForwardedFor .

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

This is the example we examined in the Whats Middleware? section earlier. It sets request.META['REMOTE_ADDR'] based on request.META['HTTP_X_FORWARDED_FOR'] , if the latter is set. This is useful if youre sitting behind a reverse proxy that causes each requests REMOTE_ADDR to be set to .

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



This middleware does not validate HTTP_X_FORWARDED_FOR .

这个middleware并 验证 HTTP_X_FORWARDED_FOR 的合法性。

If youre not behind a reverse proxy that sets HTTP_X_FORWARDED_FOR automatically, do not use this middleware. Anybody can spoof the value of HTTP_X_FORWARDED_FOR , and because this sets REMOTE_ADDR based on HTTP_X_FORWARDED_FOR , that means anybody can fake his IP address.

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

Only use this middleware when you can absolutely trust the value of HTTP_X_FORWARDED_FOR .

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

Session Support Middleware


Middleware class: django.contrib.sessions.middleware.SessionMiddleware .

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

This middleware enables session support. See Chapter 12 for details.

这个中间件激活会话支持功能. 细节请参见第12章。

Sitewide Cache Middleware


Middleware class: django.middleware.cache.CacheMiddleware .

ZV3FvR <a href=”“>haiujfhxgmcm</a>, [url=]nvlhddespdgs[/url], [link=]siljtaebxpgm[/link],

This middleware caches each Django-powered page. This was discussed in detail in Chapter 13.


Transaction Middleware


Middleware class: django.middleware.transaction.TransactionMiddleware .

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

This middleware binds a database COMMIT or ROLLBACK to the request/response phase. If a view function runs successfully, a COMMIT is issued. If the view raises an exception, a ROLLBACK is issued.

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

The order of this middleware in the stack is important. Middleware modules running outside of it run with commit-on-save the default Django behavior. Middleware modules running inside it (coming later in the stack) will be under the same transaction control as the view functions.

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

See Appendix C for more about information about database transactions.


X-View Middleware

X-View 中间件

Middleware class: django.middleware.doc.XViewMiddleware .

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

This middleware sends custom X-View HTTP headers to HEAD requests that come from IP addresses defined in the INTERNAL_IPS setting. This is used by Djangos automatic documentation system.

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

Whats Next?


Web developers and database-schema designers dont always have the luxury of starting from scratch. In the next chapter, well cover how to integrate with legacy systems, such as database schemas youve inherited from the 1980s.


Copyright 2006 Adrian Holovaty and Jacob Kaplan-Moss.
This work is licensed under the GNU Free Document License.
Hosting graciously provided by media temple
Chinese translate hosting by 粤ICP备16122281号-1