Django中间件
1. 中间件概念:
一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。说的直白一点中间件是帮助我们在视图函数执行之前和执行之后都可以做一些额外的操作,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在请求的特定的时间去执行这些方法。
中间件缺点:中间件为全局修改,若设置太过复杂的中间件,会降低效率影响性能
2.配置项
中间件:配置项为列表(有序);每个字符串是一个类,也就是一个中间件
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', #安全校验相关 'django.contrib.sessions.middleware.SessionMiddleware', # 必须放在request.session前,否则request中没有session属性,进而报错 # Django中的request传递的是同一个request 'django.middleware.common.CommonMiddleware', # 通用 'django.middleware.csrf.CsrfViewMiddleware', # 校验csrf_token 'django.contrib.auth.middleware.AuthenticationMiddleware', # 认证相关 'django.contrib.messages.middleware.MessageMiddleware', # 信息相关 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'mymiddlewares.mm.M1', #自定义的,可以是一个package中的py文件 'mm.M2', #自定义的,可以是一个放在根目录下的py文件 ]
3. 如何自定义中间件:
a. 按照格式要求写一个类(放在根目录下的py文件,或者跟目录下新建的package中的py文件中)
from django.utils.deprecation import MiddlewareMixin class MD1(MiddlewareMixin): # 继承MiddlewareMixin的类 def process_request(self, request): print("MD1里面的 process_request") def process_response(self, request, response): print("MD1里面的 process_response") return response # process_response必须要用返回值 def process_view(self, request, view_func, view_args, view_kwargs): print("MD1 中的process_view") print(view_func, view_func.__name__) # .__name__取方法名/函数名 def process_exception(self, request, exception): print(exception) print("MD1 中的process_exception") # return HttpResponse(str(exception)) def process_template_response(self, request, response): print("MD1 中的process_template_response") return response
b. 把我们写好的类在settings.py注册到MIDDLEWARE配置项的列表中
4. 每一个中间件中五个可以被重写的方法
a. process_request(self,request)
1)何时执行:在urls.py之前执行 2)执行的顺序:按照在列表中注册的顺序依次执行 3)返回值: #1 返回None, 不做任何处理直接进行下一步 #2 返回响应对象, 直接跳出(后续中间件的process_request、不执行urls.py和views.py)返回同级中间件以及之前(根据列表)的响应
b. process_view(self, request, view_func, view_args, view_kwargs)
1)执行时间:在urls.py之后,在执行真正的视图函数之前 2)执行顺序:按照在列表中注册的顺序依次执行 3)返回值 #1 返回None, 放行 #2 返回响应对象,就直接跳出,倒序依次执行所有中间件的process_response方法
c. process_response(self, request, response)
1)何时执行:在views.py返回响应对象之后执行 2)执行的顺序:按照在列表中注册的倒序依次执行 3)返回值: 必须要有返回值,否则报错。
返回要是响应对象(HttpResponse, render, redirect),
可能是views中的响应,也可能是MIDDLEWARE中其之后(根据列表)的process_response的响应
d. process_exception(self, request, exception)
1)何时执行:只有在视图函数中出现异常了才执行 2)执行的顺序:按照中间件注册顺序的倒序执行 3)返回值: #1 返回一个None,交给下一个中间件的process_exception方法来处理异常。 #2 返回HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。 # 例如: 中间件M1在M2之前(根据列表),如果出现exception,M2的的process_exception会被触发,若其有返回值,M1中的process_exception不会被执行
e. process_template_response(self,request,response)(用的比较少)
1)何时执行: 当response是TemplateResponse对象(由视图函数或者中间件产生);
视图函数返回的对象有render()方法。
process_template_response是在视图函数执行完成后立即执行。 2)执行的顺序:按照中间件注册顺序的倒序执行 3)返回值:None/ Response
5. 中间件的执行流程
请求到达中间件之后,先按照正序执行每个注册中间件的process_reques方法,process_request方法返回的值是None,就依次执行,如果返回的值是HttpResponse对象,不再执行后面的process_request方法,而是执行当前对应中间件的process_response方法,将HttpResponse对象返回给浏览器。也就是说:如果MIDDLEWARE中注册了6个中间件,执行过程中,第3个中间件返回了一个HttpResponse对象,那么第4,5,6中间件的process_request和process_response方法都不执行,顺序执行3,2,1中间件的process_response方法。
process_request方法都执行完后,匹配路由,找到要执行的视图函数,先不执行视图函数,先执行中间件中的process_view方法,process_view方法返回None,继续按顺序执行,所有process_view方法执行完后执行视图函数。加入中间件3 的process_view方法返回了HttpResponse对象,则4,5,6的process_view以及视图函数都不执行,直接从最后一个中间件,也就是中间件6的process_response方法开始倒序执行。
process_template_response和process_exception两个方法的触发是有条件的,执行顺序也是倒序。总结所有的执行流程如下:
6. 例子
1 from django.utils.deprecation import MiddlewareMixin 2 from django.shortcuts import HttpResponse, redirect 3 4 class M1(MiddlewareMixin): 5 6 """ 7 五个支持被重写的方法 8 1. process_request(self,request) 9 2. process_view(self, request, view_func, view_args, view_kwargs) 10 3. process_template_response(self,request,response) 11 4. process_exception(self, request, exception) 12 5. process_response(self, request, response) 13 """ 14 # 取代views中的装饰器,通过设置中间件process_request为每个视图加上验证功能 15 16 def process_request(self,request): 17 print("哈哈哈,我是自定义中间件M1中的一个卖报的小行家!") 18 next_url = request.path_info 19 if not request.path_info.startswith("/login/"): # 排除login页面 20 login_flag = request.session.get("login", "") 21 if not login_flag: 22 rep = redirect("/login/?next={}".format(next_url)) 23 return rep 24 25 def process_view(self, request, view_func, view_args, view_kwargs): 26 print("这是m1中的process_view方法") 27 print(view_func.__name__) 28 29 # 捕捉异常 30 def process_exception(self, request, exception): 31 print("我是m1里面的process_exception") 32 if isinstance(exception, ValueError): #如果捕捉的异常是ValueError的一个实例 33 print(exception) 34 return HttpResponse("404") 35 36 def process_response(self, request, response): 37 """ 38 :param request: 本次请求 39 :param response: 响应 40 :return: 41 """ 42 print("m1中的process_response方法") 43 return response #必须要有response 44 # rep = HttpResponse("O98K") 45 # return rep
7. 其他
(1)以下两个导入方式导入的HttpResponse是一个
from django.http import HttpResponse from django.shortcuts import HttpResponse
(2) Django中的request传递的是同一个request
(3)importlib (字符串 -> 导入对应的模块/包)
https://pymotw.com/3/py-modindex.html
#1 importlib.machinery(): to find the supported types for the current platform, and the parameters for loading them. #2 importlib.import_module(): 写倒入模块的绝对名或相对名,如果找不到就会报错 #3 importlib.reload(): 重新加载已经在的模块 #4 importlib.find_loader(): to get a loader for a module #5 loader = importlib.find_loader('example') loader.load_module(): to retrieve the module
(4)时间间隔
(5) 登陆校验的装饰器
def check_login(func): def inner(request, *args, **kwargs): # 如果用def inner(*args,**kwargs): 接受所有参数,request为args[0] login_flag = request.session.get("login", "") if login_flag.upper() == "OK": return func(request, *args, **kwargs) else: return redirect("/login/") return inner
(6)pycharm使用秘籍
http://edu.51cto.com/lecturer/7891220.html