django Middleware

Middleware简介

Middleware是一个轻量级的,全局性质的Django请求/响应处理钩子框架。所谓钩子框架是指在request请求到达Django之后,views视图处理之前,提前对request请求进行预处理,如禁止某些主机访问当前URL,如果request中没有携带cookie则只允许访问特定的网页等。当views处理完request请求后返回httpresponse,middleware还可以对views返回的httpresponse进行处理改变输出结果等。

用户编写自己的Middleware时,Django为我们提供五个方法:

  • process_request(request)
  • process_response(request,response)
  • process_view(request, view_func, view_args, view_kwargs)
  • process_exception(request, exception)
  • process_template_response(request, response)

在介绍五个方法之前,先准备下Django环境。

工具环境:windws 7 x64  pycharm2018  python 3.6.2  django 1.11.20

关于上述 软件安装这里不做演示。

Django程序:

项目:django_temp

在django_temp/django_temp/目录下创建views.py,views.py内容如下:

from django.shortcuts import render

# Create your views here.
def index(request):
    print('views function')
    return render(request,'index.html')

在django_temp/templates/目录下创建index.html,index.html内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>

在django_temp/django_temp/urls.py中添加路由。

from django.conf.urls import url
from django.contrib import admin
from . import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', views.index),
]

此时我们运行django,访问http://127.0.0.1:8000/index/应该能看到如下:

上面的测试环境搭建好后,下面来了解django为我们写中间件时提供的五个方法。

process_request

process_request(request):request请求到达Django之后,views视图处理之前,提前对request请求进行一些预处理,如禁止某些主机访问当前URL。

参数:

request:wsgi格式的request请求。

返回值:必须是None或者Httpresponse,如果返回None。则按照Middleware的注册顺序依次向下执行,直到运行View视图。如果返回的是Httpresponse,则在当前Middleware处返回Httpresponse。

示例一 Middleware的加载顺序。

在django_temp/目录下创建Middleware_test.py,该文件内容如下:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class Middleware01(MiddlewareMixin):
    def process_request(self,request):
        print('request  01')

class Middleware02(MiddlewareMixin):
    def process_request(self,request):
        print('request  02')

Middleware_test.py便是我们编写的Middleware,此时我们还需要在django_temp/django_temp/settings.py中激活我们的Middleware。

在settings.py中添加如下内容:

添加完成后,重启django,在访问http://127.0.0.1:8000/index/,我们会看到如下:

为了验证Middleware是否是按照顺序从上至下,我们将Middleware01和Middleware02换下位置,在此查看结果

示例二  直接返回Httpresponse

修改Middleware_test.py中内容:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class Middleware01(MiddlewareMixin):
    def process_request(self,request):
        print('request  01')
        return HttpResponse('<h1>Middleware 01</h1>')

class Middleware02(MiddlewareMixin):
    def process_request(self,request):
        print('request  02')
        return HttpResponse('<h1>Middleware 02</h1>')

访问http://127.0.0.1:8000/index/,我们会看到如下:

从上图我们可以看出,在执行到Middleware02后,该类的process_request返回了Httpresponse对象,下面的Middleware01和views没有被执行。

示例三   一个没什么意义的示例

修改Middleware_test.py文件内容如下:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class Middleware01(MiddlewareMixin):
    def process_request(self,request):
        print('request  01')
        return HttpResponse('<h1>Middleware 01</h1>')

class Middleware02(MiddlewareMixin):
    def process_request(self,request):
        print('request  02')
        return HttpResponse('<h1>Middleware 02</h1>')
    def process_response(self,request,response):
        print('response 02')
        return response

访问http://127.0.0.1:8000/index/,我们会看到如下:

上图可以看出它与示例二的区别在于,在Middleware02中添加了process_response方法,该方法在process_request后被执行了。

小结:

middleware可以request请求到达django之后views之前,对request进行预处理。

middleware需要在settints.py的MIDDLEWARE中注册激活,MIDDLEWARE是一个列表,middleware的执行顺序是MIDDLEWARE列表的索引顺序。

process_request方法如果返回None,按MIDDLEWARE列表中的顺序执行完middleware后,到urls.py中匹配views,执行views。

process_request方法如果返回Httpresponse,如果当前middleware中没有process_response方法,则从此处middleware一层一层向上返回。

process_request方法如果返回Httpresponse,如果当前middleware中有process_response方法,则从当前middleware中process_response处一层一层向上返回。

process_response

process_response(request,response):views处理request后,返回HttpResponse对象。该方法可以对HttpResponse对象进行修改。

参数:

request:web服务器发来的wsgi request。

response:HttpResponse对象。

返回值:HttpResponse对象。

接着上面的项目,调整Middleware_test.py文件。

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class Middleware01(MiddlewareMixin):
    def process_request(self,request):
        print('request  01')
    def process_response(self,request,response):
        print('response 01')
        return response

class Middleware02(MiddlewareMixin):
    def process_request(self,request):
        print('request  02')
    def process_response(self,request,response):
        print('response 02')
        return response

上述这两个middleware什么都不做,只是打印下加载顺序。访问http://127.0.0.1:8000/index/

示例一  修改默认输出

我们通过process_response修改views的HttpResponse对象中的content内容。修改后的Middleware_test.py文件如下:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class Middleware01(MiddlewareMixin):
    def process_request(self,request):
        print('request  01')
    def process_response(self,request,response):
        print('response 01')
        return response

class Middleware02(MiddlewareMixin):
    def process_request(self,request):
        print('request  02')
    def process_response(self,request,response):
        print('response 02')
        response.content = '<h1>Hello World, process_response</h1>'
        return response

访问http://127.0.0.1:8000/index/,我们会看到如下:

示例二  了解middle中HttpRespone的加载顺序。

修改middleware_test.py中的内容如下:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class Middleware01(MiddlewareMixin):
    def process_request(self,request):
        print('request  01')
    def process_response(self,request,response):
        response.content = '<h1>process_response 01</h1>'
        return response

class Middleware02(MiddlewareMixin):
    def process_request(self,request):
        print('request  02')
    def process_response(self,request,response):
        response.content = '<h1>process_response 02</h1>'
        return response

访问http://127.0.0.1:8000/index/,我们会看到如下:

下面我们把settings.py中Middleware01和Middleware02换一下位置。查看结果:

我们发现结果发生了改变,因此我们可以得出如下结论:

middleware可以对view返回的HttpResponse进行修改。

HttpResponse的执行顺序是settings.py中MIDDLEWARE列表从最后一个元素(middleware)开始一直到第一个元素(middleware)结束。

request和HttpResponse执行流程我们可以画一个简易图,如下:

process_view

process_view(request, view_func, view_args, view_kwargs):请求到来之后,view视图之前,可以改变参数的值来改变最终的输出结果。

参数:

request:web server发来的wsgi request(HttpRequest对象)。

view_func:视图函数的内存地址。

view_args:给view_func传递的位置参数(元组)。

view_kwargs:给view_func传递的关键字参数(字典)。

返回值:如果返回None,按照MIDDLEWARE的顺序执行其它middleware,直到运行响应的view视图。如果返回的是HttpResponse,则按照MIDDLEWARE的顺序从最后一个middleware开始倒叙执行。

示例 一 

查看process_view的执行顺序,修改middleware_test.py文件如下:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class Middleware01(MiddlewareMixin):
    def process_request(self,request):
        print('process_request  01')
    def process_view(self,request, view_func, view_args, view_kwargs):
        print('process_view  01')
    def process_response(self,request,response):
        print('process_response  01')
        return response

class Middleware02(MiddlewareMixin):
    def process_view(self,request, view_func, view_args, view_kwargs):
        print('process_view  02')
    def process_request(self,request):
        print('process_request  02')
    def process_response(self,request,response):
        print('process_response  02')
        return response

访问http://127.0.0.1:8000/index/,我们会看到如下:

示例 二

process_view修改关键字参数。

在修改之前,我们需要调整下views.py中的index方法和urls.py中的路由,settings.py,middleware_test.py。

views.py内容如下:

from django.shortcuts import render

# Create your views here.
def index(request,server_id):
    # 打印修改后的server_id
    print('views function server_id:%s' %server_id)
    return render(request,'index.html')

urls.py内容如下:

from django.conf.urls import url
from django.contrib import admin
from . import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/(?P<server_id>d+)/', views.index),
    # url(r'^index/', views.index),
]

.middleware_test.py内容如下:

from django.utils.deprecation import MiddlewareMixin

class Middleware01(MiddlewareMixin):
    def process_view(self,request, view_func, view_args, view_kwargs):
        print(f'前端发来的server_id: {view_kwargs["server_id"]}' )
        view_kwargs['server_id'] = 100

访问http://127.0.0.1:8000/index/,我们会看到如下:

示例  二

使用process_view返回HttpResponse。

修改middleware_test.py文件内容如下:

from django.utils.deprecation import MiddlewareMixin

class Middleware01(MiddlewareMixin):
    def process_view(self,request, view_func, view_args, view_kwargs):
        print(f'前端发来的server_id: {view_kwargs["server_id"]}')
        return view_func(request,server_id=100)

访问http://127.0.0.1:8000/index/,我们会看到如下:

示例  三

process_view执行后settings.py中MIDDLEWARE中的middleware加载顺序。

修改middleware_test.py文件内容如下:

from django.utils.deprecation import MiddlewareMixin

class Middleware01(MiddlewareMixin):
    def process_response(self,request,response):
        print('process_response  01')
        return response
    def process_view(self,request, view_func, view_args, view_kwargs):
        print(f'前端发来的server_id: {view_kwargs["server_id"]}')
        return view_func(request,server_id=100)

class Middleware02(MiddlewareMixin):
    def process_response(self,request,response):
        print('process_response  02')
        return response

访问http://127.0.0.1:8000/index/,我们会看到如下:

由上面三个示例小结如下:

process_view在request到达django之后,view视图之前,提前对view进行预处理。

如果process_view返回HttpReponse,将按settings.py中的MIDDLEWARE元素从最后一个middleware执行到第一个middleware。

process_view可以修改关键字参数。

process_exception

process_exception(request, exception):view视图中抛出异常后,process_exception可以对异常进行后续处理。

参数:

request:web server发来的wsgi request(HttpRequest对象)。

exception:view视图函数中引发的Exception对象。

返回值:None或者是HttpResponse对象,如果是None是返回django默认异常处理,如果返回的是HTTPResponse是用户自定义的异常处理页面。

示例   处理抛出的异常

1、正常抛出异常

修改view内容如下:

from django.shortcuts import render

# Create your views here.
def index(request,server_id):
    # 抛出异常
    int('a')
    return render(request,'index.html')

将settings.py中我们写的middleware先注释掉。访问http://127.0.0.1:8000/index/,我们会看到如下:

上面的是正常情况的异常,下面我们对异常进行后续处理返回一个界面。

修改middleware_test.py内容如下:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class Middleware01(MiddlewareMixin):
    def process_exception(self,request,exception):
        print(exception)
        return HttpResponse('<h1>发生了一个异常</h1>')

访问http://127.0.0.1:8000/index/,我们会看到如下:

小结:

process_exception可以对view视图引发的异常进行后续处理。

process_exception执行顺序按settings.py中的MIDDLEWARE元素从最后一个middleware执行到第一个middleware。

process_template_response

process_template_response(request, response):该方法在view执行完之后被执行,前提是view返回的是一个TemplateResponse对象,或者是一个由render方法的HttpResponse对象。如果不是这两种则不会被执行。

参数:

request:web server发来的wsgi request(HttpRequest对象)。

response:TemplateResponse对象或者是带有reader方法的HttpResponse对象中。

返回值:HttpResponse对象

示例  使用process_template_response

使用前我们需要修改view.py文件和middleware_test.py文件。

view.py文件内容如下:

from django.shortcuts import HttpResponse

def index(request,server_id):
    '''
    因为process_template_response执行的前提是view必须返回TemplateResponse对象
    或者是HttpResponse对象中带有render方法,我们知道view视图函数必须返回HttpResponse对象
    所以我们先创建一个HttpResponse对象。
    在定义一个render方法,该方法是最终的输出结果,所以需要返回个HttpResponse对象
    在HttpResponse对象中添加方法render。
    这样在调用view视图函数后便会执行middleware中的process_template_response方法了
    :param request:
    :param server_id:
    :return:
    '''
    ret = HttpResponse("")
    def render():
        print("这里是自定义的render方法")
        return HttpResponse("<h1>Hello World</h1>")

    ret.render = render
    return ret

middleware_test.py文件内容如下:

from django.utils.deprecation import MiddlewareMixin

class Middleware01(MiddlewareMixin):
    def process_template_response(self,request, response):
        print('process_template_response 被调用了')
        return response

访问http://127.0.0.1:8000/index/,我们会看到如下:

示例 二

查看middleware中的执行顺序,修改middleware_test.py和settings.py文件

middleware_test.py文件内容如下:

from django.utils.deprecation import MiddlewareMixin

class Middleware01(MiddlewareMixin):
    def process_template_response(self,request, response):
        print('process_template_response 被调用了')
        return response
    def process_response(self,request, response):
        print('process_response 01')
        return response

class Middleware02(MiddlewareMixin):
    def process_response(self,request, response):
        print('process_response 02')
        return response

访问http://127.0.0.1:8000/index/,我们会看到如下:

小结:

process_template_response该方法在view执行完之后被执行,前提是view返回的是一个TemplateResponse对象,或者是一个由render方法的HttpResponse对象。如果不是这两种则不会被执行。

process_template_response执行完成后,按settings.py中的MIDDLEWARE最后一个middleware执行到第一个middleware。

总结:

process_response,process_view,process_exception,process_template_response返回的结果必须是一个HttpResponse对象。

process_view,process_exception,process_template_response执行完成后,都会按照settings.py中MIDDLEWARE中注册的middleware倒叙执行(如果有process_response)process_response。

process_request的执行顺序是按照settings.pyMIDDLEWARE中注册的先后顺序执行middleware。

process_request返回None,则会正常执行其它middleware,路由匹配,执行view视图函数。

process_request返回HttpResponse,则直接在当前middleware的位置倒叙向上执行process_response(如果middleware中有process_response)。

原文地址:https://www.cnblogs.com/caesar-id/p/12897825.html