Django Middleware
Django Middleware定义:
django中间件是一个钩子框架,它们可以介入Django 的请求和响应处理过程。它是一个轻量级、底层的“插件”系统,用于在全局修改Django 的输入或输出。
中间件中的5个钩子(方法)和应用的顺序
在请求阶段中,调用视图之前,Django会按照MIDDLEWARE_CLASSES中定义的顺序自顶向下应用中间件。会用到两个钩子:
- process_request()
- process_view()
在响应阶段中,调用视图之后,中间件会按照相反的顺序应用,自底向上。会用到三个钩子:
- process_exception()(仅当视图抛出异常的时候) #可以捕获报错,从而减少在View中定义未知错误
- process_template_response()(仅用于模板响应) #很少用
- process_response()
中间件中可以定义5个方法,分别是:
process_request(self,request)
process_view(self, request, view_func, view_func_args, view_func_kwargs) //request请求完了后,再返回执行第一个view直到最后一个view
process_template_response(self,request,response)
process_exception(self, request, exception)
process_response(self, request, response)
以上方法的返回值可以是None和HttpResonse对象,如果是None,则继续按照django定义的规则向下执行,如果是HttpResonse对象,则直接将该对象返回给用户。
MiddleWare应用顺序测试
注意: 下图是在Django1.10以后的流程, 在Django1.10之前的版本的时候是直接返回到最后一个中间件的response,然后向上依次返回,最后到发起请求。
测试1(定义三个中间件,观察返回结果)
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class Row1(MiddlewareMixin):
def process_request(self, request):
print("中间件1-Request")
def process_response(self, request, response):
print("中间件1-Response")
return response
def process_view(self, request, callback, callback_args, callback_kwagrs):
print("中间件1-View")
class Row2(MiddlewareMixin):
def process_request(self, request):
print("中间件2-Request")
def process_response(self, request, response):
print("中间件2-Response")
return response
def process_view(self, request, callback, callback_args, callback_kwagrs):
print("中间件2-View")
class Row3(MiddlewareMixin):
def process_request(self, request):
print("中间件3-Request")
def process_response(self, request, response):
print("中间件3-Response")
return response
def process_view(self, request, callback, callback_args, callback_kwagrs):
print("中间件3-View")
测试结果:
中间件1-Request
中间件2-Request
中间件3-Request
中间件1-View
中间件2-View
中间件3-View
中间件3-Response
中间件2-Response
中间件1-Response
测试2(在中间件2的process_request中直接return,观察结果)
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class Row1(MiddlewareMixin):
def process_request(self, request):
print("中间件1-Request")
def process_response(self, request, response):
print("中间件1-Response")
return response
def process_view(self, request, callback, callback_args, callback_kwagrs):
print("中间件1-View")
class Row2(MiddlewareMixin):
def process_request(self, request):
print("中间件2-Request")
return HttpResponse("error!!!")
def process_response(self, request, response):
print("中间件2-Response")
return response
def process_view(self, request, callback, callback_args, callback_kwagrs):
print("中间件2-View")
class Row3(MiddlewareMixin):
def process_request(self, request):
print("中间件3-Request")
def process_response(self, request, response):
print("中间件3-Response")
return response
def process_view(self, request, callback, callback_args, callback_kwagrs):
print("中间件3-View")
测试结果:
中间件1-Request
中间件2-Request
中间件2-Response
中间件1-Response
自定义中间件案例:
可以自定义Middleware通过request.path_info request.META 等request头部信息来实现自己的业务逻辑,比如识别 客户端Agent(手机客户端),识别客户端IP 等等。
案例1:
在setting中添加版本号,在渲染静态css,js的路径时附带版本号,例如“example.v1124.css”,然后再在request请求中祛除掉版本号。为什么要这么做呢?因为这样做的话在前端输出静态文件的路径就会加上就会加上版本号,这样当开发出新的静态文件的时候,客户端就会强制刷新本地的缓存,为了达到这个目的就要首先要在settings文件中配置,这样就可以每次修改的时候直接从settings读取版本号了。
步骤1: settings.py
#Static file version number STATIC_VERSION = 'v123'
步骤2: views.py
#要想在settings文件中取到变量就要先导入模块 from django.conf import settings #在settings配置文件中取得静态文件版本号 version = settings.STATIC_VERSION #下面这个函数是例子..... def index(request): return render(request, "index.html", {"static_version": version})
步骤3: html
<link rel="stylesheet" href="{% static "css/bootstrap-{{ version }}.css" %}"> <script src="{% static "js/jquery-{{ version }}.js" %}"></script>
到这一步,当你打开浏览器后台的时候你就会发现你的路径就会改变了,但是这个时候当你再刷新的时候就会出现静态文件找不到的问题了,为什么会这样呢?因为这个时候的浏览器会继续按照新的路径去请求静态文件,但是你会发现你静态文件的物理地址其实是没有改变过的,所以这时候你请求的是新的地址,自然也就取不到了,那么我们该怎么办呢?OK 我们开始编写Meddleware!
步骤4: 编写meddelware
from django.utils.deprecation import MiddlewareMixin
import re
class StaticVersionMiddleware(MiddlewareMixin): def process_request(self, request): if request.path_info.startswith("/app01/static/"): request.path_info = re.sub(r'\.v\d+', "", request.path_info)
步骤5:
现在按照之前流程图里面的我们是不是就完成了呢?显然不是嘛~仅仅这样做的话你会发现你的Middleware并没有起作用,我们用pdb断点调试发现,其实我们并没有捕获到静态文件的request请求,为什么会这样呢?因为是一个系统app捣的鬼! INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.messages', #就是下面的这个app 'django.contrib.staticfiles', # Uncomment the next line to enable the admin: # 'django.contrib.admin', # Uncomment the next line to enable admin documentation: # 'django.contrib.admindocs', ) 如果我们没有额外配置static路径的话,系统会默认将所有静态文件的请求发给staticfiles来处理,所以我们要将这个app静止掉,这样就可以完美的接收请求了,鉴于我百度了很多方法都没有给出路由文件的配置方法,这样会导致结果会不正确,所以我就把路由中的配置方法贴出来了。 static_dir = os.path.join(os.path.dirname(__file__),'../app01/static') urlpatterns += patterns('', (r'^app01/static/(?P<path>.*)$','django.views.static.serve',{'document_root':static_dir}), )