中间件是面向切面编程的好例子,它是一个能够介入Django的request和response处理过程的钩子框架,一个轻量级、底层的“插件”系统,用于在全局修改Django的输入或输出。python
要使用中间件,首先要在settings中设置:web
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
上述是Django项目的默认设置,每一项字符串都表明一个中间件。中间件就像洋葱同样,每一个中间件都是一个层,在用户请求阶段,调用定义的view函数以前,请求以自上而下的顺序经过全部的层,view函数处理以后,响应以自下而上的顺序经过全部的层,期间通过的每一个中间件都会对请求或者响应进行处理。django
要自定义中间件,写一个类便可:编程
class SimpleMiddleware(object): def __init__(self, get_response): self.get_response = get_response # One-time configuration and initialization. def __call__(self, request): # Code to be executed for each request before # the view (and later middleware) are called. response = self.get_response(request) # Code to be executed for each request/response after # the view is called. return response
__init__初始化方法只在web服务启动时调用一次,get_response参数是必需的,这个参数指的是下一个中间件或者view函数(若是是最后一个中间件)。session
每一个请求都会调用一次__call__,先对请求作处理,而后将请求发往下一个中间件(get_response),一层层的迭代,就像栈同样,返回时对响应作处理。框架
若是其中一个层的中间件决定短路并返回响应而不调用其get_response,那么该层下面的层都不会看到请求或响应,只有该中间件和之上的中间件才会看到响应。函数
短路能够起到一些ip过滤之类的效果:spa
class IpFilterMiddleware(object): def __init__(self, get_response): self.get_response = get_response # One-time configuration and initialization. def __call__(self, request): if request.META.has_key('HTTP_X_FORWARDED_FOR'): ip = request.META['HTTP_X_FORWARDED_FOR'] else: ip = request.META['REMOTE_ADDR'] if ip == '127.0.0.1': return HttpResponse('You are forbidden') response = self.get_response(request) return response
设置:插件
MIDDLEWARE = [ 'path.to.IpFilterMiddleware',
上述中间件把当地访问拦截了,实际应用中能够为ip创建黑名单,而后检查访问ip是否在黑名单中。debug
咱们也能够将中间件标记为未使用,只要在__init__中引起一个django.core.exceptions.MiddlewareNotUsed异常便可。
除了前面描述的基本请求/响应中间件模式,您还能够向基于类的中间件添加三种其余特殊方法:
process_view(request, view_func, view_args, view_kwargs)
在调用view以前被调用。
它应该返回一个None 或一个HttpResponse对象。 若是返回None,Django 将会继续处理这个请求,执行其它的process_view() 中间件,而后调用对应的视图。 若是它返回一个HttpResponse对象,也会起到短路做用。
process_exception(request, exception)
当一个view引起异常时,Django会调用process_exception()来处理。返回一个None或一个HttpResponse对象。若是返回HttpResponse对象,会将响应交给处理响应的中间件处理。因为处理响应时是从下到上的,此层以上的process_exception()是不会被调用的。
process_template_response(request, response)
response参数应该是一个由view或者中间件返回的TemplateResponse对像(或等价的对象)。
若是响应的实例有render()方法,process_template_response()会在view恰好执行完毕以后被调用。这个方法必须返回一个实现了render方法的响应对象。
开发阶段一般将DEBUG打开以调试错误,部署时会关闭DEBUG以避免用户看到错误信息。咱们能够写一个异常中间件,让有权限的管理员能够在DEBUG关闭的状况下看到错误信息,这样就避免了切换的麻烦;
import sys from django.views.debug import technical_500_response class ExceptionMiddleware(object): def __init__(self, get_response): self.get_response = get_response # One-time configuration and initialization. def __call__(self, request): response = self.get_response(request) return response def process_exception(self, request, exception): if request.user.is_admin: return technical_500_response(request, *sys.exc_info())
置于中间件的最底层,这样管理员能够看到用户没法看到的错误详细信息。
关于异常处理
Django会自动将视图或中间件引起的异常转换为具备错误状态代码的适当HTTP响应,这种转换发生在每一个中间件以前和以后,咱们是无需使用try捕捉异常的。
在Django 1.10版本以前,中间件设置名为MIDDLEWARE_CLASSES,是长这样的:
class SessionMiddleware(object): def __init__(self): engine = import_module(settings.SESSION_ENGINE) self.SessionStore = engine.SessionStore def process_request(self, request): session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME) request.session = self.SessionStore(session_key) def process_response(self, request, response): ...
Django提供django.utils.deprecation.MiddlewareMixin以简化与MIDDLEWARE和旧的MIDDLEWARE_CLASSES兼容的中间件类。 Django中包含的全部中间件类都兼容这两种设置。
class MiddlewareMixin(object): def __init__(self, get_response=None): self.get_response = get_response super(MiddlewareMixin, self).__init__() def __call__(self, request): response = None if hasattr(self, 'process_request'): response = self.process_request(request) if not response: response = self.get_response(request) if hasattr(self, 'process_response'): response = self.process_response(request, response) return response
新版继承自MiddlewareMixin的SessionMiddleware:
class SessionMiddleware(MiddlewareMixin): def __init__(self, get_response=None): self.get_response = get_response engine = import_module(settings.SESSION_ENGINE) self.SessionStore = engine.SessionStore def process_request(self, request): session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME) request.session = self.SessionStore(session_key) def process_response(self, request, response): ...
混合类提供一个__init__()方法,它接受一个可选的get_response参数,并将其存储在self.get_response中。
__call__()方法:
在大多数状况下,继承这种混合类足以使旧式中间件与新系统兼容,具备足够的向后兼容性。
上述的异常中间件也能够写为:
from django.utils.deprecation import MiddlewareMixin class ExceptionMiddleware(MiddlewareMixin): def process_exception(self, request, exception): if request.user.is_admin: return technical_500_response(request, *sys.exc_info())
使用MIDDLEWARE和MIDDLEWARE_CLASSES之间的行为差异:
StreamingHttpResponse并无content属性,使用时必须加以判断:
if response.streaming: response.streaming_content = wrap_streaming_content(response.streaming_content) else: response.content = alter_content(response.content)
用生成器处理过大的内容:
def wrap_streaming_content(content): for chunk in content: yield alter_content(chunk)