11.Django|中间件
中间件
文件夹为middlewareDemo
中间件是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。
Django的中间件的定义:
Middleware is a framework of hooks into Django’s request/response processing. <br>It’s a light, low-level “plugin” system for globally altering Django’s input or output.
Django默认的Middleware
:
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'app01.my_middlewares.CustomerMiddleware', #这是自定义的 'app01.my_middlewares.CustomerMiddleware2', #自定义的中间件 ] from django.middleware.security import SecurityMiddleware #可以点进去查看SecurityMiddleware这个类 from django.contrib.sessions.middleware import SessionMiddleware
每一个中间件都有具体的功能。
django默认7个中间件,其实就是一个类。
django请求周期
接收到了socket,拿到了请求信息,wsgire做了一个封装request的操作(解析数据,解析成一个request的对象)----------------->>>会进入中间件,执行每一个中间件的process_request,它是依次执行的。------>
路由层做分发,分发完了交给视图进行处理,视图要响应一个值,如果是字符串就原路返回了,它返回的时候是依次执行process_response这个方法 走出中间件 ---->
wsgiref封装了数据,(http协议必须按照响应体的响应格式进行发,如果不按照那个格式发浏览器无法解析;wsgiref封装协议,响应首行、响应体、响应体)
把请求体交给浏览器----》拿到响应体浏览器解析为一个页面给用户看。
中间件不管你访问的请求路径是什么,只要请求访问的是这个服务器就必然会通过这个中间件来的时候经过process_respongse,回去的时候经过process_request,属于全局性逻辑处理。
自定义中间件
中间件一共有四个方法:-
process_request
process_view
process_exception
process_response
当用户发起请求的时候会依次经过所有的的中间件,这个时候的请求时process_request,最后到达views的函数中,views函数处理后,在依次穿过中间件,这个时候是process_response,最后返回给请求者。 这个中间件的类不一定都有procee_request和procee_response,也可能只有一个,没有process_response返回的时候就不经过了。
上述截图中的中间件都是django中的,我们也可以自己定义一个中间件,我们可以自己写一个类,但是必须继承MiddlewareMixin
需要导入:
from django.utils.deprecation import MiddlewareMixin
process_response是在中间件里边一层层的传,先到第一个个中间件,再到第二个,是依次执行的
视图函数在校验时候,可以把校验写在某个中间件里边去,在这里边写一次,进来的所有请求都可以校验下,不用一个个加装饰器了。
from django.utils.deprecation import MiddlewareMixin class CustomerMiddleware(MiddlewareMixin): #让它继承下这个类 def process_request(self, request): print('CustomerMiddleware1 process_request') #默认返回None;然后比如你访问index页面,它就会执行这句话了,走了precess_request class CustomerMiddleware2(MiddlewareMixin): def process_request(self, request): print('CustomerMiddleware2 process_request')
'app01.my_middlewares.CustomerMiddleware', #在settins里边的MIDDLEWARE把它加进去。 'app01.my_middlewares.CustomerMiddleware2', #这两个中间件里边的process_request依次执行; 它是全局的逻辑处理
打印:
CustomerMiddleware1 process_request CustomerMiddleware2 process_request index #先走中间件,再进视图函数打印它 [15/Jun/2018 10:01:38] "GET /index/ HTTP/1.1" 200 5
中间件的作用:
大部分视图要做的工作,我们可以放到中间件里边去,比如关于session的验证:好像视图函数必须是在登录之后才能看这个页面,必须在每个视图函数里边加判断或者加装饰器,二三十个视图函数都挂一个装饰器login_request,如果把校验放到中间件里边去,在这里边定义一次,所有的请求都进来之后都会校验它是否登录了
from django.shortcuts import render, HttpResponse # Create your views here. def index(request): print('index') #进来的时候先执行中间件CustomerMiddleware的process_request,再走中间件CustomerMiddleware2的process_request,再打印index;走的时候是反过来走中间件 return HttpResponse("INDEX") #响应体就是INDEX这个字符串,先执行后边的中间件,先交给CustomerMiddleware2的process_response,执行完自己的逻辑,
又把response交给了它上一个中间件CustomerMiddleware的process_response,然后执行它的逻辑打印,就这样子传接力棒 def index_new(request): return HttpResponse('index_new')
from django.utils.deprecation import MiddlewareMixin class CustomerMiddleware(MiddlewareMixin): def process_request(self, request): print('CustomerMiddleware1 process_request') def process_response(self, request,response): #response就是HttpResponse(INDEX)相应体对象 print('CustomeerMiddleware1 process_request') return response #它必须要有返回值,就像接力棒一层层传给别人;在process_request里边必须要写返回值。 class CustomerMiddleware2(MiddlewareMixin): def process_request(self, request): print('CustomerMiddleware2 process_request') def process_response(self, request,response): print('CustomeerMiddleware2 process_request') return response
打印:
CustomerMiddleware1 process_request CustomerMiddleware2 process_request index CustomeerMiddleware2 process_request CustomeerMiddleware1 process_request [15/Jun/2018 10:11:12] "GET /index/ HTTP/1.1" 200 5
#相应体就是INDEX这个字符串,先执行后边的中间件,先交给CustomerMiddleware2的process_response,执行完自己的逻辑,
又把response交给了它上一个中间件的process_response,然后执行它的逻辑打印,就这样子传接力棒
如果非要在process_request加个返回值,
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class CustomerMiddleware(MiddlewareMixin): def process_request(self, request): print('CustomerMiddleware1 process_request') return HttpResponse('forbidden..') def process_response(self, request,response): #response就是HttpResponse(INDEX)相应体对象 print('CustomeerMiddleware1 process_request') return response #它必须要有返回值,就像接力棒一层层传给别人 class CustomerMiddleware2(MiddlewareMixin): def process_request(self, request): print('CustomerMiddleware2 process_request') def process_response(self, request,response): print('CustomeerMiddleware2 process_request') return response
CustomerMiddleware1 process_request CustomeerMiddleware1 process_request [15/Jun/2018 10:29:42] "GET /index/ HTTP/1.1" 200 11 #访问http://127.0.0.1:8000/index/打印的是forbidden..,
没有执行中间件2的内容,也没有执行视图。
url请求中间件1的process_request的时候,一旦加了返回值,直接把相应体交给自己的process_response,交给浏览器。一下子给拦截了。应用场景:拦截,你的ip频率太高;session进行校验的时候没有登录也给你拦截下来。
process_view(这个视图方法)
加了process_view之后打印执行的顺序:
[15/Jun/2018 10:40:45] "GET /index/ HTTP/1.1" 200 5 CustomerMiddleware1 process_request CustomerMiddleware2 process_request CustomerMiddleware1:process_view CustomerMiddleware2:process_view index CustomeerMiddleware2 process_request CustomeerMiddleware1 process_request
#Author:Kris from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class CustomerMiddleware(MiddlewareMixin): def process_request(self, request): print('CustomerMiddleware1 process_request') #return HttpResponse('forbidden..') def process_view(self, request, callback, callback_args, callback_kwargs): print("CustomerMiddleware1:process_view") def process_response(self, request,response): #response就是HttpResponse(INDEX)相应体对象 print('CustomeerMiddleware1 process_response') return response #它必须要有返回值,就像接力棒一层层传给别人 class CustomerMiddleware2(MiddlewareMixin): def process_request(self, request): print('CustomerMiddleware2 process_request') def process_response(self, request,response): print('CustomeerMiddleware2 process_response') return response def process_view(self, request, callback, callback_args, callback_kwargs): #print("===>",callback) #===> <function index at 0x0000000003DE11E0> 视图函数 #在进入到2的视图函数的时候,把响应体体返回了 123,那么视图函数就不执行了,接着往下执行process_response print("===>",callback(callback_args)) #不走视图函数了我提前拿到它的响应结果 print("CustomerMiddleware2:process_view") ret = callback(callback_args) ###可以进行拦截。 return ret #把它的结果返回了就可以拿到视图函数里边的index了 #return HttpResponse('123')
上边的执行结果 CustomerMiddleware1 process_request CustomerMiddleware2 process_request CustomerMiddleware1:process_view index ===> <HttpResponse status_code=200, "text/html; charset=utf-8"> CustomerMiddleware2:process_view index CustomeerMiddleware2 process_response CustomeerMiddleware1 process_response
def process_view(self, request, callback, callback_args, callback_kwargs): #在进入到2的视图函数的时候,把响应体返回了 123,那么视图函数就不执行了,接着往下执行process_responseprint("CustomerMiddleware2:process_view") return HttpResponse('123') #没有走index,在这里返回了123 CustomerMiddleware1 process_request CustomerMiddleware2 process_request CustomerMiddleware1:process_view CustomerMiddleware2:process_view CustomeerMiddleware2 process_response CustomeerMiddleware1 process_response
process_exception(这个方法)
流程图如下:
当views出现错误时:
views.py def index(request): print('index') kris return HttpResponse("INDEX")
CustomerMiddleware1 process_request CustomerMiddleware2 process_request CustomerMiddleware1:process_view CustomerMiddleware2:process_view index #打印完视图就报错了,报错没有拿到响应体HttpResponse(index),直接走下面的process_exception CustomeerMiddleware2 process_exception CustomeerMiddleware1 process_exception Internal Server Error: /index/ 出错
CustomeerMiddleware2 process_response
CustomeerMiddleware1 process_response
#Author:Kris from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class CustomerMiddleware(MiddlewareMixin): def process_request(self, request): print('CustomerMiddleware1 process_request') #return HttpResponse('forbidden..') def process_view(self, request, callback, callback_args, callback_kwargs): print("CustomerMiddleware1:process_view") def process_response(self, request,response): #response就是HttpResponse(INDEX)相应体对象 print('CustomeerMiddleware1 process_request') return response #它必须要有返回值,就像接力棒一层层传给别人 def process_exception(self, request, exception): print('CustomeerMiddleware1 process_exception') class CustomerMiddleware2(MiddlewareMixin): def process_request(self, request): print('CustomerMiddleware2 process_request') def process_response(self, request,response): print('CustomeerMiddleware2 process_request') return response def process_view(self, request, callback, callback_args, callback_kwargs): #print("===>",callback) #===> <function index at 0x0000000003DE11E0> 视图函数 #在进入到2的视图函数的时候,把相应体返回了 123,那么视图函数就不执行了,接着往下执行process_response #print("===>",callback(callback_args)) print("CustomerMiddleware2:process_view") # ret = callback(callback_args) # return ret #把它的结果返回了就可以拿到index了 #return HttpResponse('123') def process_exception(self, request, exception): print('CustomeerMiddleware2 process_exception') return HttpResponse(exception) #一旦有一个return了,后边1的process_exception就不执行了;把它的错误信息给返回了呗
CustomerMiddleware1 process_request CustomerMiddleware2 process_request CustomerMiddleware1:process_view CustomerMiddleware2:process_view index CustomeerMiddleware2 process_exception #直接检测完,把它的错误捕捉到交给响应体返回了,就不执行1的process_exception了 CustomeerMiddleware2 process_response CustomeerMiddleware1 process_response [15/Jun/2018 11:34:36] "GET /index/ HTTP/1.1" 200 26
如果把写到1里边,2不要,return HttpResponse(exception)
CustomerMiddleware1 process_request CustomerMiddleware2 process_request CustomerMiddleware1:process_view CustomerMiddleware2:process_view index CustomeerMiddleware2 process_exception CustomeerMiddleware1 process_exception CustomeerMiddleware2 process_response CustomeerMiddleware1 process_response [15/Jun/2018 11:37:28] "GET /index/ HTTP/1.1" 200 26
综上:
会process_request、process_response这两个就足够了,要知道它的实现流程,process_request没有返回值,如果有返回值就原路返回了,下边的中间件、视图函数都不执行了;process_response必须要有返回值,如果它没有返回值就没人传这个接力棒response了,这个接力棒是--> 如果视图函数是正确的就是它的响应体,如果发生错误了就是exception的响应体。
如果我在process_response没有把它的 return response返回,自己定义了return HttpResponse("Hello kris"),相当于在接力的过程中我把接力棒给换了。
应用URL访问过滤 在auth_Demo文件夹里边
如果用户访问的是login视图(放过)
如果访问其他视图,需要检测是不是有session认证,已经有了放行,没有返回login,这样就省得在多个视图函数上写装饰器了!
my_middlewares.py
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse,redirect from authDemo import settings class AuthMiddleware(MiddlewareMixin): #认证 def process_request(self, request): white_list = settings.WHITE_LIST #先拿到那个白名单 if request.path in white_list: return None #相当于通过校验 if not request.user.is_authenticated: return redirect("/login/")
settings.py
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'app01.my_middlewares.AuthMiddleware', #把它加上去 ] LOGIN_URL = "/login/" #跳转到哪里由我自己决定; WHITE_LIST = ["/login/","/reg/","logout/"] #设置下白名单