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()

Django middlewware

其实可以更加的简单粗暴的把中间件理解为就是对业务逻辑的代码进行一层封装,让你能够全局的处理一些公共的东西。

自定义中间件

由上面的内容可以知道,每一个中间件其实就是一个实现了特地方法的普通的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之前执行。

  1. request是一个HttpRequest对象
  2. view_func是Django将要调用处理request的view函数。这将会是实际的调用函数本身。
  3. view_args是一些将要传递给view的参数列表。
  4. view_kwargs是参数元祖。

不管是view_args还是view_kwargs都不会包含第一个参数request.

process_view返回None或是HttpResponse对象。如果返回了None,Django将继续处理请求,执行其他中间件的process_view,最终执行确定的view函数。
如果返回了HttpResponse对象,Django同样是不会执行接下来的任何请求中间件,也不会执行相应的view。Django会执行响应中间件对这个HttpResponse对象进行处理。

在中间件的process_requestprocess_view函数访问request.POSTrequest.REQUEST的话会导致在后续的view中无法更改请求的上传处理程序,所以一般要避免在中间件中访问request.POSTrequest.REQUEST的做法。

process_template_response(request, response)

TemplateResponse后期处理函数。这里的response是一个TemplateResponse对象。这个方法执行的时机是在view层的函数刚被执行完。
如果view层执行的是render方法,那么就可以认为返回的便是TemplateResponse对象。
这个方法必须返回一个实现了render方法的响应对象。它可以通过改变response.template_nameresponse.cotext_data来改变给定的response,或者是返回一个新的响应。

process_response(request, response)

Response后期处理函数,这个方法执行的时机是响应返回浏览器之前被调用。它必须返回一个HttpResponse或者StreamingHttpResponse对象。
不同于process_requestprocess_view可能会因为前面的中间件返回了response而不执行,process_response方法总会被调用,所以这就意味着process_response方法不能依赖process_request方法(比如说在process_request中设置了属性A,那么属性A并不一定会真正的设置出来,因为前面的中间件提前结束了执行)。

process_exception(request, exception)

exception是view层函数抛出的异常。改函数执行时机为view层的代码抛出了一个异常。它返回一个NoneHttpResponse对象。
这个函数主要用来处理发生异常后的一些日志记录甚至是从异常中自动的修复等工作。
如果它返回了一个HttpResponse对象,process_template_responseprocess_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)

上面的这个例子并不是很好,可以等到后面在慢慢的优化。

posted @ 2017-04-24 22:24  听雨阁中听雨歌  Views(319)  Comments(0Edit  收藏  举报