form中间件的介绍
需求1:当有用户请求过来时,判断用户是否在白名单或者在黑名单里
需求2:当有用户请求过来时,判断用户是否已经登陆,未登陆就去登陆。 URL的白名单 url = ["/xx/", "/oo/", "/haha/"]
以上需求可通过给函数加装饰器完成,但是以后添加的视图函数可能也需要加上装饰器,这样稍微有点繁琐
1.中间件:wsgi之后 urls.py之前 在全局 操作Django请求和响应的模块!
中间件是一个用来处理Django的请求和响应的框架级别的钩子。用于在全局范围内改变Django的输入和输出。
说的直白一点中间件是帮助我们在视图函数执行之前和执行之后都可以做一些额外的操作,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在请求的特定的时间去执行这些方法。
2.Django项目的Settings.py文件中MIDDLEWARE配置项是一个列表,列表中是一个个字符串,这些字符串其实是一个个类,也就是一个个中间件。
3.查看中间件的源码,例如看csrf的
MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
]
from django.middleware.csrf import CsrfViewMiddleware
4.将文件以字符串的形式导入
# 比如文件夹day74中有个py文件form,通过字符串导入form
import importlib
fil=importlib.import_module('day74.form') 就成功导入,使用fil调用文件中的方法和属性
自定义中间件
1.在django的根目录下新建一个middlewares.py文件,存放自定义的中间件
from django.utils.deprecation import MiddlewareMixin class MD1(MiddlewareMixin): def process_request(self, request): print("MD1里面的 process_request") class MD2(MiddlewareMixin): def process_request(self, request): print("MD2里面的 process_request")
2.在settings.py的MIDDLEWARE配置项中注册上述两个自定义中间件:
MIDDLEWARE = [ 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'middlewares.MD1', # 自定义中间件MD1 'middlewares.MD2' # 自定义中间件MD2 ]
3.中间件执行:
中间件的process_request方法是在执行视图函数之前执行的。
当配置多个中间件时,会按照MIDDLEWARE中的注册顺序,也就是列表的索引值,从前到后依次执行的。
不同中间件之间传递的request都是同一个对象
中间件的5种方法
中间件可以定义五个方法,分别是:(主要的是process_request和process_response)
process_request(self,request)
process_view(self, request, view_func, view_args, view_kwargs)
process_template_response(self,request,response)
process_exception(self, request, exception)
process_response(self, request, response)
以上方法的返回值可以是None或一个响应对象(HttpResponse,render,redirect),如果是None,则继续按照django定义的规则向后继续执行,
如果是reponse对象,则直接将该对象返回给用户,不再执行其他中间件的方法。
1.process_request(self, request)
执行顺序:
按照注册的顺序(在settings.py里面设置中 从上到下的顺序)
何时执行:
请求从wsgi拿到之后
返回值:
返回None,继续执行后续的中间件的process_request方法
返回Response对象 , 不执行后续的中间件的process_request方法,Django将不执行视图函数,而将相应对象返回给浏览器。
2.process_response(self, request,response)
执行顺序:
按照注册顺序的倒序(在settings.py里面设置中 从下到上的顺序)
何时执行:
请求有响应的时候(只要有响应,最后到要走这个).在视图函数之后执行的
返回值:
必须返回一个response对象
3.process_view(self, request, view_func, view_args, view_kwargs):
request: 浏览器发来的请求对象 view_func: 将要执行的视图函数的名字 view_args: 将要执行的视图函数的位置参数 view_kwargs: 将要执行的视图函数的关键字参数 执行顺序: 按照注册的顺序(在settings.py里面设置中 从上到下的顺序) 何时执行: 在urls.py中找到对应关系之后 在执行真正的视图函数之前 返回值: 返回None,继续执行后续的中间件的process_view方法 返回response,如果本身定义return HttpResponse("MD2:process_view"), 而process_response中定义 return HttpResponse("hahahaha"),则 页面上显示hahahaha,只要有响应就会走process_response。不再走另一个中间件的process_view
4.process_exception(self, request, exception)
执行顺序: 按照注册顺序的倒序(在settings.py里面设置中 从下到上的顺序) 何时执行: 视图函数中抛出异常的时候才执行 返回值: 返回None,继续执行后续中间件的process_exception 返回response,Django将调用模板和中间件中的process_response方法,并返回给浏览器,不再执行其他的process_exception 在视图函数中抛出一个异常: def index(request): print("app01 中的 index视图") raise ValueError("呵呵") return HttpResponse("O98K")
5.process_template_response(self, request, response)
执行顺序:
按照注册顺序的倒序(在settings.py里面设置中 从下到上的顺序)
何时执行:
视图函数返回的对象有一个render()方法,视图函数执行完,在执行视图函数返回的响应对象的render方法之前
返回值:
返回None,继续执行后续中间件的process_exception
返回response,
中间件5个方法的顺序
1. process_request
去urls.py找到对应关系后,执行process_view
2. process_view
执行完process_view后去view中执行函数
3. 有异常就执行 process_exception
4. 如果视图函数返回的响应对象有render方法,就执行process_template_response
5. process_response
执行流程:
1.请求到达中间件之后,先按照正序执行每个注册中间件的process_reques方法
如果MIDDLEWARE中注册了6个中间件,执行过程中,第3个中间件返回了一个HttpResponse对象
那么第4,5,6中间件的process_request和process_response方法都不执行,
顺序执行3,2,1中间件的process_response方法。
2.process_request方法都执行完后,匹配路由,找到要执行的视图函数,先不执行视图函数,先执行中间件中的process_view方法,process_view方法返回None,继续按顺序执行,
所有process_view方法执行完后执行视图函数。
如果中间件3 的process_view方法返回了HttpResponse对象,则4,5,6的process_view以及视图函数都不执行,直接从中间件6的process_response方法开始倒序执行。
class MD1(MiddlewareMixin): def process_request(self, request): print("MD1里面的 process_request") def process_response(self, request, response): print("MD1里面的 process_response") return response def process_view(self, request, view_func, view_args, view_kwargs): print("-" * 80) print("MD1 中的process_view") print(view_func, view_func.__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 class MD2(MiddlewareMixin): def process_request(self, request): print("MD2里面的 process_request") pass def process_response(self, request, response): print("MD2里面的 process_response") return response def process_view(self, request, view_func, view_args, view_kwargs): print("-" * 80) print("MD2 中的process_view") print(view_func, view_func.__name__) def process_exception(self, request, exception): print(exception) print("MD2 中的process_exception") def process_template_response(self, request, response): print("MD2 中的process_template_response") return response views.py中 def index(request): print("app01 中的 index视图") def render(): print("in index/render") return HttpResponse("O98K") rep = HttpResponse("OK") rep.render = render return rep 结果: MD2里面的 process_request MD1里面的 process_request -------------------------------------------------------------------------------- MD2 中的process_view <function index at 0x000001C111B97488> index -------------------------------------------------------------------------------- MD1 中的process_view <function index at 0x000001C111B97488> index app01 中的 index视图 MD1 中的process_template_response MD2 中的process_template_response in index/render MD1里面的 process_response MD2里面的 process_response
中间件版验证登陆
urls.py
from django.conf.urls import url from app01 import views urlpatterns = [ url(r'^index/$', views.index), url(r'^login/$', views.login, name='login'), ]
class AuthMD(MiddlewareMixin): white_list = ['/login/', ] # 白名单 balck_list = ['/black/', ] # 黑名单 def process_request(self, request): from django.shortcuts import redirect, HttpResponse next_url = request.path_info print(request.path_info, request.get_full_path()) if next_url in self.white_list or request.session.get("user"): return elif next_url in self.balck_list: return HttpResponse('This is an illegal URL') else: return redirect("/login/?next={}".format(next_url))
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', 'middlewares.AuthMD', ]
from django.shortcuts import render, HttpResponse, redirect def index(request): return HttpResponse('this is index') def home(request): return HttpResponse('this is home') def login(request): if request.method == "POST": user = request.POST.get("user") pwd = request.POST.get("pwd") if user == "Q1mi" and pwd == "123456": # 设置session request.session["user"] = user # 获取跳到登陆页面之前的URL next_url = request.GET.get("next") # 如果有,就跳转回登陆之前的URL if next_url: return redirect(next_url) # 否则默认跳转到index页面 else: return redirect("/index/") return render(request, "login.html")