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>Titletitle>
head>
<body>
<h1>Hello Worldh1>
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('Middleware 01
') class Middleware02(MiddlewareMixin): def process_request(self,request): print('request 02') return HttpResponse('Middleware 02
')
访问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('Middleware 01
') class Middleware02(MiddlewareMixin): def process_request(self,request): print('request 02') return HttpResponse('Middleware 02
') 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 = 'Hello World, process_response
' 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 = 'process_response 01
' return response class Middleware02(MiddlewareMixin): def process_request(self,request): print('request 02') def process_response(self,request,response): response.content = 'process_response 02
' 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\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('发生了一个异常
')
访问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("Hello World
") 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)。