1.定义
一个用来处理Django的请求和响应的框架级别的钩子(函数),相对比较轻量级,并且在全局上改变django的输入与输出(使用需谨慎,否则影响性能)
直白的说中间件就是帮助我们在视图函数执行之前和执行之后做一些额外操作
2.用处
用户登录
日志记录
权限管理
请求验证(post)
一般对所有请求做批量处理的时候用中间件,单独对某几个函数处理直接使用装饰器
3.用法说明
我们使用django一直就在使用中间件,打开django的setting文件,当中的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',
]
列表当中的每个字符串,其实是一个个类,即一个个中间件,它们正常的执行顺序都是自上而下的
中间件中,我们可以定义五个方法,分别是:
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)
其中最常用的是process_request和process_response
以上方法的返回值可以是None或一个HttpResponse对象,如果是None,则继续按照django定义的规则向后继续执行,如果是HttpResponse对象,则直接将该对象返回给用户。
3.1 图例说明
这里还是先来看看django的生命周期图例
先从上图了解django执行流程,每一步起了那些关键性作用,然后我们再来讨论每一个中间件
3.2 process_request与process_response
process_request有一个request参数,这个和视图函数中的request参数一模一样
process_response有两个参数,一个是request,一个是response,request和前面一致,response是视图函数返回的HttpResponse对象
代码实例
我在应用下创建了一个middlewares文件,用于存放中间件
from django.utils.deprecation import MiddlewareMixin
# 注意导入路径
class MD1(MiddlewareMixin):
def process_request(self, request):
print('我是MD1的process_request')
def process_response(self, request, response):
print('我是MD1的process_response')
return response
class MD2(MiddlewareMixin):
def process_request(self, request):
print('我是MD2的process_request')
def process_response(self, request, response):
print('我是MD2的process_response')
return response
setting.py文件配制添加的中间价位置
访问一个视图,终端输出结果:
总结:
1.process_request方法,是在视图函数执行之前执行的,当配制多个中间件时,会按照MIDDLEWARE中的注册顺序,也就是列表的索引值,从前到后依次执行的
返回值是None,继续往后执行,返回值是HttpResponse的对象,执行对应中间件的process_response方法
2.对于视图函数,是在process_request执行完毕后开始执行的
3.process_response方法,是在视图函数之后执行的,而且多个中间件中的process_response方法是按照MIDDLEWARE中的注册顺序倒序执行的
3.3 process_view
process_view(self, request, view_func, view_args, view_kwargs)
该方法有四个参数:
request是HttpRequest对象。
view_func是Django即将使用的视图函数。 (它是实际的函数对象,而不是函数的名称作为字符串。)
view_args是将传递给视图的位置参数的列表.
view_kwargs是将传递给视图的关键字参数的字典。 view_args和view_kwargs都不包含第一个视图参数(request)。
代码实例
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, redirect, HttpResponse
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('我是MD1的process_view')
print(view_func, view_func.__name__)
class MD2(MiddlewareMixin):
def process_request(self, request):
print('我是MD2的process_request')
# return HttpResponse('MD2process_request')
def process_response(self, request, response):
print('我是MD2的process_response')
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print('我是MD2的process_view')
print(view_func, view_func.__name__)
效果
总结:
1.process_view是在视图执行前执行的,process_request之后,执行顺序按照MIDDLEWARE中的注册顺序从前到后顺序执行的
3.4 process_exception
process_exception(self, request, exception)
该方法两个参数:
一个HttpRequest对象
一个exception是视图函数异常产生的Exception对象。
该方法只有在视图函数中出现异常才会执行,如果视图函数中无异常,process_exception方法不执行。
代码实例
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, redirect, HttpResponse
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('我是MD1的process_view')
print(view_func, view_func.__name__)
def process_exception(self, request, exception):
print(exception, '这是MD1的process_exception')
# return HttpResponse(str(exception))
class MD2(MiddlewareMixin):
def process_request(self, request):
print('我是MD2的process_request')
# return HttpResponse('MD2process_request')
def process_response(self, request, response):
print('我是MD2的process_response')
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print('我是MD2的process_view')
print(view_func, view_func.__name__)
# return HttpResponse('这是MD2的process_view')
def process_exception(self, request, exception):
print(exception, '这是MD2的process_exception')
return HttpResponse(str(exception)) # 返回一个响应对象
在视图函数index中抛出一个异常
def index(request):
print('app01中的index视图')
raise ValueError("6666")
return HttpResponse('这是index页面')
此时结果:
# 可以从结果看出来,exception接收的就是异常信息,我们的MD1并没有return HttpResponse(str(exception)) ,但是它却也抛出了异常信息
总结:
1.只要视图函数报错了才执行
2.在视图函数之后,process_response之前
3.5 process_template_response(不常用)
process_template_response是在视图函数执行完成后立即执行,但是它有一个前提条件,那就是视图函数返回的对象有一个render()方法
4.中间件执行流程
请求到达中间件之后,如果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方法开始倒序执行。
中间件的执行顺序:
5.中间件实现登录验证
以下代码还具有阻隔功能,即必须先登录才能访问主页,直接从url访问index会拒绝请求
代码实例:
urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('login/', views.login),
path('logout/', views.logout),
path('index/', views.index),
path('home/', views.home),
]
views.py
from django.shortcuts import render, HttpResponse, redirect
from django.contrib import auth
import json
# Create your views here.
def login(request):
if request.method == "POST":
user = request.POST.get("user")
pwd = request.POST.get("pwd")
ret = {"status": 0, 'url': ''}
if user == "xiao" and pwd == "123":
# 设置session
request.session["user"] = user
ret['status'] = 1
# 跳转到index页面
ret['url'] = '/index/'
return HttpResponse(json.dumps(ret))
return render(request, "login.html")
def logout(request): # 注销
auth.logout(request)
return redirect("/login/")
def index(request):
return HttpResponse('this is index <a href="/logout/">注销</a>')
def home(request):
return HttpResponse('this is home')
应用app01下的middlewares.py
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, redirect, HttpResponse
class AuthMD(MiddlewareMixin): # 验证登录
white_list = ['/login/', ] # 白名单
black_list = ['/black/', ] # 黑名单
ret = {"status": 0, 'url': '', 'msg': ''} # 默认状态
def process_request(self, request): # 请求之前
request_url = request.path_info # 获取请求路径
# get_full_path()表示带参数的路径
print(request.path_info, request.get_full_path())
# 黑名单的网址限制访问
if request_url in self.black_list:
self.ret['msg'] = "这是非法请求"
self.ret['url'] = "/login/"
# 白名单的网址或者登陆用户不做限制
# 判断是否在白名单内或者已经有了session(表示已经登录了)
elif request_url in self.white_list or request.session.get("user"):
return None
else:
self.ret['msg'] = "请登录后,再访问!"
self.ret['url'] = "/login/"
# 错误页面提示
return render(request, "jump.html", self.ret)
settings.py的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.middlewares.AuthMD', # 自定义中间件AuthMD
]
jump.html,用来做中间件不通过时,js跳转
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<link rel="stylesheet" href="http://mishengqiang.com/sweetalert/css/sweetalert.css">
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script src="http://mishengqiang.com/sweetalert/js/sweetalert-dev.js"></script>
<div>
{#获取错误信息#}
<input type="hidden" id="msg" value="{{ msg }}">
<input type="hidden" id="url" value="{{ url }}">
</div>
<script>
$(function () {
var msg = $("#msg").val();
var url = $("#url").val();
console.log(msg);
console.log(url);
if (msg.length > 0) { //判断是否有错误信息
swal({
title: msg,
text: "2秒后自动关闭。",
type: 'error',
timer: 2000,
showConfirmButton: false
}, function () {
window.location.href = url; //跳转指定url
});
}
})
</script>
</body>
</html>
# 这里的定制化错误信息效果相当不错,可以拿来借鉴
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>登录</title>
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/css/signin.css">
</head>
<body>
<div class="container">
<form class="form-signin">
{% csrf_token %}
<h2 class="form-signin-heading">请登录</h2>
<label for="inputUser" class="sr-only">用户名</label>
<input type="text" id="inputUser" class="form-control" placeholder="用户名" required="" autofocus="" name="user">
<label for="inputPassword" class="sr-only">密码</label>
<input type="password" id="inputPassword" class="form-control" placeholder="密码" required="" name="pwd">
<div class="checkbox">
<label>
<input type="checkbox" value="remember-me"> 记住我
</label>
</div>
<input class="btn btn-lg btn-primary btn-block" id="login" value="登陆">
</form>
</div> <!-- /container -->
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
{#必须先引入jquery,再引入cookie#}
<script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
{#sweetalert插件#}
<link rel="stylesheet" href="http://mishengqiang.com/sweetalert/css/sweetalert.css">
<script src="http://mishengqiang.com/sweetalert/js/sweetalert-dev.js"></script>
<script>
//ajax在发送之前,做的header头。csrfSafeMethod是一个方法名,用来调用的
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
//这些HTTP方法不需要CSRF保护
// 匹配method的请求模式,js正则匹配用test
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$('#login').click(function () {
$.ajax({
url: '/login/',
type: 'post',
//增加X-CSRFToken的请求头
headers:{ "X-CSRFToken":$.cookie('csrftoken') },
data: {
user: $('[name="user"]').val(),
pwd: $('[name="pwd"]').val()
},
success: function (data) {
data = JSON.parse(data);
if (data.status) {
swal({
title: '登录成功',
type: 'success', //展示成功的图片
timer: 500, //延时500毫秒
showConfirmButton: false //关闭确认框
}, function () {
window.location.href = data.url; //跳转后台首页
});
}
else {
sweetAlert("登录失败!", data.msg, "error");
}
}
})
})
</script>
</body>
</html>
更多专业前端知识,请上 【猿2048】www.mk2048.com