一、中间件简介
是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。
如果你想修改请求,例如被传送到view中的HttpRequest对象。 或者你想修改view返回的HttpResponse对象,这些都可以通过中间件来实现。
可能你还想在view执行之前做一些操作,这种情况就可以用 middleware来实现。
二、自定义中间件
自定义步骤:
1 写一个类,继承MiddlewareMixin
from django.utils.deprecation import MiddlewareMixin
2 里面写方法,至少写一个process_request(请求来了,一定会触发它的执行)
def process_request(self, request):
pass
3 在setting中配置(注意,放在前和放在后)
MIDDLEWARE = [
...
'app01.mymiddle.MyMiddleware1',
...
]
中间件执行顺序:
请求来的时候从上往下执行
请求走的时候,从下往上执行
# 其实本质就是起了一个for循环,根据这个列表的索引一次执行
所以我们可以在自定义中间件中写着两个方法达到自己的全局控制
正因为这个执行顺序,所以自定义的中间件语句放的位置也就需要注意。比如我们的自定义中间件想要在请求来的时候对session先进行处理,那么我们就需要把中间件放在session的中间件后面,因为如果在它的前面根本就无法从request.session里取出值来。
三、中间件的方法
1.需要知道的方法
多个中间件的方法执行顺序
请求来的时候,从上往下执行各个中间件的process_request
请求走的时候,从下往上执行各个中间件的process_response
重点掌握:
1 process_request(request对象)
2 process_response(request对象,response对象)
3 process_view
4 process_exception
了解:知道还有一个就行了,不需要记住
process_template_response(self,request,response)
该方法对视图函数返回值有要求,必须是一个含有render方法类的对象,才会执行此方法
process_request可以干什么?
-写一个中间件,不管前端用什么编码,在requset.data中都有post的数据
-频率限制(限制某个ip地址,一分钟只能访问5次)
-登录认证(只要没登录,重定向到login路径)、
-记录用户访问日志(ip,时间,访问路径)
process_response可以干什么?内部有response对象
-统一给所有(某几个路径)加cookie
-统一给所有(某几个路径)加响应头
process_view 路由匹配成功和视图函数执行之前执行(callback就是视图函数)
def process_view(self, request, callback, callback_args, callback_kwargs):
# callback就是views视图函数
# callback_args,无名分组的参数
# callback_kwargs,有名分组的参数
# 可以在这里面改变视图函数触发的时机,且可以在视图函数前后加代码,实现一个装饰器的功能
res=callback(request)
print("中间件的process_view")
return res
process_exception 视图函数出错,会执行它(全局异常捕获)(记录日志,哪个ip地址,访问哪个路径,出的错)
# 全局异常捕获
def process_exception(self, request, exception):
print(exception)
return render(request,'error.html')
2.request与response流程图
不是所有的请求都一定会走到视图函数,如下例子
# 自定义中间件
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class Md1(MiddlewareMixin):
def process_request(self,request):
print("Md1请求")
def process_response(self,request,response):
print("Md1返回")
return response
class Md2(MiddlewareMixin):
def process_request(self,request):
print("Md2请求")
#return HttpResponse("Md2中断")
def process_response(self,request,response):
print("Md2返回")
return response
# 视图函数
def index(request):
print("view函数...")
return HttpResponse("OK")
正常执行流程
Md1请求
Md2请求
view函数...
Md2返回
Md1返回
如果当请求到达请求2的时候直接不符合条件返回,即return HttpResponse("Md2中断"),程序将把请求直接发给中间件2返回,然后依次返回到请求者,结果如下
Md1请求
Md2请求
Md2返回
Md1返回
由此总结一下:
- 中间件的process_request方法是在执行视图函数之前执行的。
- 当配置多个中间件时,会按照MIDDLEWARE中的注册顺序,也就是列表的索引值,从前到后依次执行的。
- 不同中间件之间传递的request都是同一个对象
多个中间件中的process_response方法是按照MIDDLEWARE中的注册顺序倒序执行的,也就是说第一个中间件的process_request方法首先执行,而它的process_response方法最后执行,最后一个中间件的process_request方法最后一个执行,它的process_response方法是最先执行。
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)。
Django会在调用视图函数之前调用process_view方法。利用process_view方法可以改变视图函数的执行顺序。
它应该返回None或一个HttpResponse对象。 如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个HttpResponse对象,Django不会调用适当的视图函数。 它将执行中间件的process_response方法并将应用到该HttpResponse并返回结果。
注意:process_view如果有返回值,会越过其他的process_view以及视图函数,但是所有的process_response都还会执行。
4.process_exception执行流程
process_exception(self, request, exception)
该方法两个参数:
一个HttpRequest对象
一个exception是视图函数异常产生的Exception对象。
这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。
四、CSRF_TOKEN跨站请求伪造
csrf攻击:https://blog.csdn.net/xiaoxinshuaiga/article/details/80766369
csrf攻击django是怎么预防的,从我们的网站发出的post请求都要带一个csrf为key,随机字符串为value的隐藏标签,如果有这个标签就可以进行提交,如果没有的话就禁止访问。
django已经帮助我们集结了csrf攻击,是中间件django.middleware.csrf.CsrfViewMiddleware解决的
1.在Ajax-data中的应用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="/static/jquery-3.3.1.js"></script>
<title>Title</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<p>用户名:<input type="text" name="name"></p>
<p>密码:<input type="text" name="password" id="pwd"></p>
<p><input type="submit"></p>
</form>
<button class="btn">点我</button>
</body>
<script>
$(".btn").click(function () {
$.ajax({
url: '',
type: 'post',
data: {
'name': $('[name="name"]').val(),
'password': $("#pwd").val(),
'csrfmiddlewaretoken': $('[name="csrfmiddlewaretoken"]').val()
'csrfmiddlewaretoken':'{{ csrf_token }}' 也行
},
success: function (data) {
console.log(data)
}
})
})
</script>
</html>
2.在Ajax-cookie中的应用
要导包jquery.cookie.js
获取cookie:document.cookie
是一个字符串,可以自己用js切割,也可以用jquery的插件
获取cookie:$.cookie('csrftoken')
设置cookie:$.cookie('key','value')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="/static/jquery-3.3.1.js"></script>
<script src="/static/jquery.cookie.js"></script>
<title>Title</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<p>用户名:<input type="text" name="name"></p>
<p>密码:<input type="text" name="password" id="pwd"></p>
<p><input type="submit"></p>
</form>
<button class="btn">点我</button>
</body>
<script>
$(".btn").click(function () {
var token=$.cookie('csrftoken')
//var token='{{ csrf_token }}'
$.ajax({
url: '',
headers:{'X-CSRFToken':token},
type: 'post',
data: {
'name': $('[name="name"]').val(),
'password': $("#pwd").val(),
},
success: function (data) {
console.log(data)
}
})
})
</script>
</html>
放在cookie里
3.使用总结
-form表单提交
-在form表单中 {% csrf_token%}
-ajax提交(需要自己把csrf串写进去)
方式一:放到data中
$.ajax({
url: '/csrf_test/',
method: 'post',
data: {'name': $('[name="name"]').val(),
'password': $('[name="password"]').val(),
'csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val()
},
success: function (data) {
console.log('成功了')
console.log(data)
},
error: function (data) {
console.log('xxxxx')
console.log(data)
}
})
方式二:放到data中
'csrfmiddlewaretoken':'{{ csrf_token }}'
方式三:放到头中
headers:{'X-CSRFToken':'{{csrf_token}}'},
4.全局启用/禁用,局部禁用/启用
使用django内置的装饰器
from django.views.decorators.csrf import csrf_exempt,csrf_protect
# 全局启用,局部禁用(中间件不能注释,这个视图函数,已经没有csrf校验了)
@csrf_exempt
def csrf_test(request):
if request.method=='GET':
return render(request,'csrf_test.html')
else:
name=request.POST.get('name')
password=request.POST.get('password')
print(name)
print(password)
return HttpResponse('登录成功')
# 全局禁用,局部使用csrf(中间件注释,但这个视图还是需要csrf校验)
@csrf_protect
def csrf_test(request):
if request.method=='GET':
return render(request,'csrf_test.html')
else:
name=request.POST.get('name')
password=request.POST.get('password')
print(name)
print(password)
return HttpResponse('登录成功')
# 高级(装逼)的使用方式,在urls.py中
path('csrf_test/', csrf_exempt(views.csrf_test))