中间件
内容概要
- 什么是中间件?
- 简介
- django 请求生命周期流程
- 自定义中间件
- django 中间件掌握方法
- process_request
- process_response
- django 中间件了解方法
- process_view
- process_template_response
- process_exception
- csrf 跨站请求伪造
- csrf 校验
- csrf 相关装饰器
内容详细
什么是中间件?
简介
Django 中间件是 Django 后端的门户
- 请求来的时候得先经过中间件校验处理才能到达 django 后端
- 响应走的时候也需要经过中间件处理后才能发送出去
官方的说法:中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。每个中间件组件都负责做一些特定的功能。但是由于其影响的是全局,所以需要谨慎使用,使用不当会影响性能。
中间件是帮助我们在视图函数执行之前和执行之后都可以做一些额外的操作,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在请求的特定的时间去执行这些方法。
Django 中间件有哪些?我们可以在配置文件 settings.py 中看到:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', # 安全相关
'django.contrib.sessions.middleware.SessionMiddleware', # 处理 session
'django.middleware.common.CommonMiddleware', # 路由带 '/' 处理
'django.middleware.csrf.CsrfViewMiddleware', # 跨站请求伪造验证与处理·
'django.contrib.auth.middleware.AuthenticationMiddleware', # auth 模块功能
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
在 MIDDLEWARE 列表里面注册的中间件其实是一个一个类,这些类继承了 MiddlewareMixin 类,可以作为中间件对请求数据和响应数据进行处理,下面我们来看一下 SessionMiddleware 中间件
from django.contrib.sessions.middleware import SessionMiddleware
from django.utils.deprecation import MiddlewareMixin
class SessionMiddleware(MiddlewareMixin): # 中间件类继承 MiddlewareMixin 类
def process_request(self, request):
session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
request.session = self.SessionStore(session_key)
def process_response(self, request, response):
return response
中间件中主要会使用五个方法:
这五个方法都是中间件类声明的同名方法,是利用了面向对象的鸭子类型(多态),只要调用就可以循环挨个调用
- process_request 形参有 request,返回值为None则继续执行下一步,为Httpresponse则会拦截请求
- process_response 形参有 request、response,返回值为 response,也可以是自定义的Httpresponse,狸猫换太子
- process_view
- process_template_response
- process_exception
django 请求生命周期流程
Django 中间件是 Django 后端的门户
中间件的执行顺序:请求来时根据注册列表的顺序自上而下执行,响应走时根据注册列表倒序执行(自下而上)
自定义中间件
根据中间件的特性,我们来尝试自己定义一个中间件
步骤:
- 1、在项目文件夹或应用文件夹下创建任意名称的文件夹
- 2、文件夹内创建一个任意名称的 py 文件,用于存放自定义中间件类
- 3、在 py 文件书写需要的自定义类(这些类需要继承 MiddlewareMixin 类)
- 4、在类中创建 process_request\process_response 等方法用于处理请求响应数据
- 5、在 settings.py 配置文件的 MIDDLEWARE 列表中注册自定义中间件(添加路径字符串)
中间件 process_request 和 process_response 执行顺序:
django 中间件掌握方法
process_request
process_request有一个参数,就是request,这个request和视图函数中的request是一样的(在交给Django后面的路由之前,对这个request对象可以进行一系列的操作)。
由于request对象是一样的,所以我们可以对request对象进行一系列的操作,包括request.变量名=变量值,这样的操作,我们可以在后续的视图函数中通过相同的方式即可获取到我们在中间件中设置的值。
它的返回值可以是None也可以是HttpResponse对象。返回值是None的话,按正常流程继续走,交给下一个中间件处理,如果是HttpResponse对象,Django将不执行视图函数,只执行完该中间件的process_response 就将相应对象返回给浏览器。
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class MD1(MiddlewareMixin): # 继承 MiddlewareMixin 类
def process_request(self, request): # 接收 request 参数
print(request.body, id(request))
print('第一个自定义中间件的 request 方法')
# 该方法返回 None 继续执行下一个中间件
# 如果返回一个 HttpResponse 对象,则只执行 MD1 的 process_response 然后返回浏览器
return HttpResponse('在第一个中间件被拦截')
def process_response(self, request, response): # 接收 request、response 参数
print(request.body, id(request))
print('第一个自定义中间件的 response 方法')
return response # 需要返回 response 响应数据,返回其它HttpResponse则是换了响应数据
# 可以在此定义多个自定义中间件
class MD2(MiddlewareMixin): # 继承 MiddlewareMixin 类
def process_request(self, request): # 接收 request 参数
print(request.body, id(request))
print('第二个自定义中间件的 request 方法')
# 该方法返回 None 继续执行下一个中间件
def process_response(self, request, response): # 接收 request、response 参数
print(request.body, id(request))
print('第二个自定义中间件的 response 方法')
return response # 需要返回 response 响应数据,返回其它HttpResponse则是换了响应数据
被拦截后的打印结果:
b'' 2523468077520
第一个自定义中间件的 request 方法
b'' 2523468077520
第一个自定义中间件的 response 方法
前端:
利用这一点,可以实现 :书写反爬措施,写黑名单做全局校验
小总结:
- 中间件的process_request方法是在执行视图函数之前执行的。
- 当配置多个中间件时,会按照MIDDLEWARE中的注册顺序,也就是列表的索引值,从前到后依次执行的。
- 不同中间件之间传递的request都是同一个对象
ps: flask 框架中也有个中间件,不过它的规律是:只要返回数据了就必经过所有中间件里类似 process_response 方法。
process_response
多个中间件中的process_response方法是按照MIDDLEWARE中的注册顺序倒序执行的,也就是说第一个中间件的process_request方法首先执行,而它的process_response方法最后执行,最后一个中间件的process_request方法最后一个执行,它的process_response方法是最先执行。
定义process_response方法时,必须给方法传入两个形参,request和response。request就是上述例子中一样的对象,response是视图函数返回的HttpResponse对象(也就是说这是Django后台处理完之后给出一个的一个具体的视图)。该方法的返回值(必须要有返回值)也必须是HttpResponse对象。如果不返回response而返回其他对象,则浏览器不会拿到Django后台给他的视图,而是我的中间件中返回的对象
django 中间件了解方法
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方法。
它应该返回None或一个HttpResponse对象。 如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个HttpResponse对象,那么将不会执行Django的视图函数,而是直接在中间件中掉头,倒叙执行一个个process_response方法,最后返回给浏览器
process_template_response
process_template_response(self, request, response)
它的参数,一个HttpRequest对象,response是TemplateResponse对象(由视图函数或者中间件产生)。
process_template_response是在视图函数执行完成后立即执行,但是它有一个前提条件,那就是视图函数返回的对象有一个render()方法(或者表明该对象是一个TemplateResponse对象或等价方法)。
process_exception
process_exception(self, request, exception)
该方法两个参数:
一个HttpRequest对象
一个exception是视图函数异常产生的Exception对象。
这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。
csrf 跨站请求伪造
钓鱼网站:
不法分子搭建一个与官网一模一样的网站页面,欺骗用户进入该网站输入用户信息并提交发起请求,钓鱼网站再把用户信息和修改过的信息一起向官网服务器发起请求,官网服务端无法辨认请求的真假,是否为自家网页发出,做出了错误操作
csrf 校验
要解决上面问题,我们需要在响应给用户的网站页面上添加一个唯一标识,每次用户向后端发送请求时都必须带上这个唯一标识,在后端做对标识做校验,如果请求数据中没有带上标识或者标识不正确则拒绝该请求。
添加 csrf 方式:
前端:在form表单中添加,这样响应给用户的网页中就带有了 csrf
<form id="MyForm">
{% csrf_token %} <!--添加 csrf_token -->
{% for form in form_obj %}
<div class="form-group">
<label for="{{ form.auto_id }}">{{ form.label }}</label>
{{ form }}
<span style="color: red" class="pull-right"></span>
</div>
{% endfor %}
如果是以 ajax 方式提交数据,在 data 参数中也要带上 csrf 键值:
$.ajax({
url: '',
type: 'post',
data: {
'username': $('#username').val(),
'password': $('#password').val(),
'code': $('#code').val(),
'csrfmiddlewaretoken': '{{ csrf_token }}' // 要记得添加单引号
},
success: function (args){
console.log(args)
if(args.code==1000){
window.location.href = args.url
}else {
$('#id_commit').next().text(args.msg)
}
}
})
csrf 相关装饰器
导入模块:
from django.views.decorators.csrf import csrf_protect, csrf_exempt
需求:
1、网站整体都不校验csrf,只有几个视图函数需要校验 csrf
2、网站整体都校验csrf,只有几个视图函数不需要校验 csrf
csrf_protect:需要校验 csrf
添加该装饰器,只有这个视图函数要做 csrf 校验
@csrf_protect
def home(request):
article_obj = models.Article.objects.all()
return render(request, 'home.html', locals())
csrf_exempt:忽视校验 csrf
添加该装饰器,这个视图函数就不用做 csrf 校验了
@csrf_exempt
def home(request):
article_obj = models.Article.objects.all()
return render(request, 'home.html', locals())
注意
如果是在 CBV 中,记得三种添加装饰器的方法
-
给 CBV 添加装饰器
类中的方法与独立函数不完全相同,因此不能直接将函数装饰器应用于类中的方法 ,我们需要先将其转换为方法装饰器。
Django中提供了method_decorator装饰器用于将函数装饰器转换为方法装饰器。需要导入 django.utils.decorators 装饰器模块 的 method_decorator 函数
给 CBV 添加方法装饰器的三种方式:
1、在类中函数头上添加(不推荐使用)
from django.utils.decorators import method_decorator
class MyClass(View):
@method_decorator(csrf_exempt)
def get(self, request):
return HttpResponse('get 请求')
@method_decorator(csrf_exempt)
def post(self, request):
return HttpResponse('post 请求')
2、在类头上添加
这种方法还可以指定给不同的方法添加不同的装饰器
@method_decorator(csrf_exempt, name='get')
@method_decorator(csrf_exempt, name='post')
class MyClass(View):
def get(self, request):
return HttpResponse('get 请求')
def post(self, request):
return HttpResponse('post 请求')
3、根据源码,在 dispatch 函数上添加装饰器,既可以做到作用于所有类方法
class MyClass(View):
@method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
pass
def get(self, request):
return HttpResponse('get 请求')
def post(self, request):
return HttpResponse('post 请求')