Django的中间件
Django的中间件
Django对于中间件的定义为:
中间件是用来处理请求和响应的插件机制,用来全局的处理Django的输入输出。
每一个中间件都会有自己专门的功能。
激活中间件
要激活Django
中的中间件,需要将他们添加到Django的MIDDLEWARE_CLASSESS
配置项中。
在MIDDLEWARE_CLASSES
中,每一个中间件的表示方法都是使用它在Python中的路径全名去表示。比如下面的就是默认的配置项:
MIDDLEWARE_CLASSES = (
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
由于某一个中间件可能依赖其他的中间件,所以中间件配置项中的中间件的声明要有一定的顺序。比如AuthenticationMiddleware
在session中存储用户的信息,所以他必须在SessionMiddleware
中间件的后面。
工作原理
Django处理请求的处理分为两部分,一部分是请求到达view
层之前,一部分是view
层之后,返回响应。
Django在处理一个请求时,会依次从上到下的处理MIDDLEWARE_CLASSES
中声明的中间件。其中中间件中的两个钩子函数会被调用:
process_request()
process_view()
在处理响应的时候,会依次从下到上的处理MIDDLEWARE_CLASSESS
中的中间件,其中起作用的函数是:
process_exception() // 当view层跑出一个异常的时候才会起作用
process_template_response() // 当返回的时候一个模版的时候才会起作用
process_response()
其实可以更加的简单粗暴的把中间件理解为就是对业务逻辑的代码进行一层封装,让你能够全局的处理一些公共的东西。
自定义中间件
由上面的内容可以知道,每一个中间件其实就是一个实现了特地方法的普通的Python类。我们可以很容易的自定义一个自己的中间件。
要定义一个自己的中间件需要实现下面的一些方法中的部分。
process_request(request)
request
预处理方法,request
是一个HttpRequest
对象。
这个方法的执行时机是当请求来临的时候,Django尚未执行view的时候。Django会传入一个HttpRequest
对象,此时如果对此对象进行修改的话会使得改变存在于此次请求全部的过程中。
process_request
返回None
或是HttpResponse
对象。
如果返回一个None
会继续处理这个请求,执行接下来的其它中间件中的process_request
,再接着执行中间件中的process_view
,最终才会去执行对应的view函数。
如果返回的是一个HttpResponse
对象,Django不会执行接下来的任何请求中间件,也不会执行相应的view。Django会执行响应中间件对这个HttpResponse
对象进行处理。
process_view(request, view_func, view_args, view_kwargs)
view
预处理函数,这个方法的执行时机是当请求经过process_request
的处理之后,到达view之前执行。
request
是一个HttpRequest
对象view_func
是Django将要调用处理request的view函数。这将会是实际的调用函数本身。view_args
是一些将要传递给view的参数列表。view_kwargs
是参数元祖。
不管是view_args
还是view_kwargs
都不会包含第一个参数request
.
process_view
返回None
或是HttpResponse
对象。如果返回了None
,Django将继续处理请求,执行其他中间件的process_view
,最终执行确定的view函数。
如果返回了HttpResponse
对象,Django同样是不会执行接下来的任何请求中间件,也不会执行相应的view。Django会执行响应中间件对这个HttpResponse
对象进行处理。
在中间件的
process_request
和process_view
函数访问request.POST
和request.REQUEST
的话会导致在后续的view中无法更改请求的上传处理程序,所以一般要避免在中间件中访问request.POST
和request.REQUEST
的做法。
process_template_response(request, response)
TemplateResponse
后期处理函数。这里的response
是一个TemplateResponse
对象。这个方法执行的时机是在view层的函数刚被执行完。
如果view层执行的是render
方法,那么就可以认为返回的便是TemplateResponse
对象。
这个方法必须返回一个实现了render
方法的响应对象。它可以通过改变response.template_name
和response.cotext_data
来改变给定的response
,或者是返回一个新的响应。
process_response(request, response)
Response
后期处理函数,这个方法执行的时机是响应返回浏览器之前被调用。它必须返回一个HttpResponse
或者StreamingHttpResponse
对象。
不同于process_request
和process_view
可能会因为前面的中间件返回了response而不执行,process_response
方法总会被调用,所以这就意味着process_response
方法不能依赖process_request
方法(比如说在process_request中设置了属性A,那么属性A并不一定会真正的设置出来,因为前面的中间件提前结束了执行)。
process_exception(request, exception)
exception
是view层函数抛出的异常。改函数执行时机为view层的代码抛出了一个异常。它返回一个None
或HttpResponse
对象。
这个函数主要用来处理发生异常后的一些日志记录甚至是从异常中自动的修复等工作。
如果它返回了一个HttpResponse
对象,process_template_response
和process_response
会被调用。
如果返回的是None
默认的异常处理机制会被触发。
init
初始化中间件。
大多数的中间件类都不需要一个初始化方法。如果缺失需要一个全局的状态就可以通过__init__
来加载。但是要记住:
Django中间件类只会初始化一次,即第一个请求到达的时候,之后的请求并不会再次初始化。
Django初始化中间件不需要任何的参数。
如果__init__
抛出了django.core.exception.MiddlewareNotUsed
,则Django不会加载这个中间件,将会从中间件处理过程中移除这个中间件。
一个简单的例子
在后端向客户端提供API接口的时候,经常会遇到用户登陆了之后需要返回一个session_id给客户端,然后客户端拿到这个session_id放到cookie中在返回回来。然而客户端并不支持原生的cookie,这个时候就可以将session_id当作一个token传回来。
class AccessTokenMiddleware(object):
# 并不建议这么做哦
def __init__(self):
self.token = None
self.user_session = None
self.session_expire = None
def process_request(self, request):
request.token_store = {}
# 并不建议这么做哦
if request.method == 'POST':
self.token = request.POST.get('token')
print 'access token middleware post'
else:
self.token = request.GET.get('token')
print 'access token middleware get'
if self.token is not None:
try:
self.user_session = TokenService()
self.user_session.get(self.token)
request.token_store = self.user_session.token_data
print request.token_store
except ObjectDoesNotExist:
pass
def process_response(self, request, response):
print 'now is in middleware token'
if self.user_session is None and len(request.token_store) > 0:
print 'token is none'
self.user_session = TokenService()
self.user_session.token_data = request.token_store
self.user_session.save()
if isinstance(response, JsonResponse):
response.content = self.setdefault_content(response.content,
'token', self.user_session.token_store.token_key)
if self.token is not None:
print 'token is not none'
self.user_session.token_data = request.token_store
self.user_session.save()
if isinstance(response, JsonResponse):
response.content = self.setdefault_content(response.content, 'token', self.token)
self.__init__()
request.token_store = {}
return response
@staticmethod
def setdefault_content(content, key, value):
json_content = json.loads(content)
json_content[key] = value
return json.dumps(json_content)
上面的这个例子并不是很好,可以等到后面在慢慢的优化。