按照官方说法:Django中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用在全局范围内改变Django的输入和输出。每个中间件组件都负责做一些特定的功能。
简单点说Django中间件就是Django的门户,主要功能有两点
- 请求来的时候需要先经过中间件才能达到真正的Django后端
- 响应走的时候最后也需要经过中间件才能发送出去
Django自带有七个中间件,在settings.py文件中,有个 MIDDLEWARE 配置项
注意: 由于中间件影响的是全局,所以需要谨慎使用
补充: MIDDLEWARE 配置项是一个列表,列表中是一个个字符串,这些字符串就是一个个类,也是一个个中间件,中间件执行的顺序请求时从上往下,响应时从下往上,可以参考Django生命周期流程图
自定义中间件的方法:
在项目名或者应用名下创建一个任意名称的文件夹
在该文件夹内创建一个任意名称的py文件
在该py文件内需要书写类(这个类必须继承MiddlewareMixin)
然后在这个类里面就可以定义五个方法了(五个方法用几个写几个即可,不用全写)
需要将类的路径以字符串的形式注册到配置文件中(这里有涉及到了另一个编程思想,后文会详细介绍)
自定义中间件5种方法特性详细介绍
重点掌握(process_request和process_response)
process_request:
当请求来的时候需要经过每一个中间件里面的process_request方法,顺序按照配置文件中注册的中间件从上往下的顺序依次执行
如果中间件里面没有定义该方法,那么直接跳过执行下一个中间件
如果该方法返回了HttpResponse对象,那么请求将不再继续往后面执行,而是直接原路返回
process_request 方法就是用来做全局相关的所有限制功能
process_response:
补充:
了解即可
process_view:
process_template_response:
process_exception
在我们刚接触Django,需要提交post请求的是时候,通常需要我们将MIDDLEWARE中的csrf中间件注销 ,这是因为在我们发送post请求的时候,不做任何处理,csrf中间件是无法校验通过的。
其主要作用是用来防止钓鱼网站
csrf原理: 在用户提交get请求获取数据时,服务器会随机生成一个唯一标识(随机字符串,每一次都不一样)返回给网站,当用户提交post请求的时候,会把将这个唯一标识携带,当经过csrf中间件的时候,会进行校验,校验成功,才会允许post请求中的数据通过。
<form action="" method="post">
{% csrf_token %}
<p>username:<input type="text" name="username"></p>
<p>target_user:<input type="text" name="target_user"></p>
<p>money:<input type="text" name="money"></p>
<input type="submit">
</form>
1. 利用标签查找获取页面上的唯一标识
data:{"username":'xxx','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},
2. 利用模板语法提供的快捷书写
data:{"username":'xxx','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},
3. 通用方式,直接拷贝js代码(官方提供)并应用到自己的html页面上即可
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
from django.views.decorators.csrf import csrf_protect,csrf_exempt
from django.utils.decorators import method_decorator
'''
csrf_protect 需要校验
针对csrf_protect符合我们之前所学的装饰器的三种玩法
csrf_exempt 忽视校验
针对csrf_exempt只能给dispatch方法加才有效
'''
1. 针对FBV: 正常使用即可
# @csrf_exempt
@csrf_protect
def transfer(request):
if request.method == 'POST':
username = request.POST.get('username')
target_user = request.POST.get('target_user')
money = request.POST.get('money')
print('%s给%s转了%s元'%(username,target_user,money))
return render(request,'transfer.html')
2. 针对CBV
from django.views import View
# @method_decorator(csrf_protect,name='post') # 针对csrf_protect 第二种方式可以
# @method_decorator(csrf_exempt,name='post') # 针对csrf_exempt 第二种方式不可以
@method_decorator(csrf_exempt,name='dispatch')
class MyCsrfToken(View):
# @method_decorator(csrf_protect) # 针对csrf_protect 第三种方式可以
# @method_decorator(csrf_exempt) # 针对csrf_exempt 第三种方式可以
def dispatch(self, request, *args, **kwargs):
return super(MyCsrfToken, self).dispatch(request,*args,**kwargs)
def get(self,request):
return HttpResponse('get')
# @method_decorator(csrf_protect) # 针对csrf_protect 第一种方式可以
# @method_decorator(csrf_exempt) # 针对csrf_exempt 第一种方式不可以
def post(self,request):
return HttpResponse('post')
importlib模块: 能够通过字符串来导入模块,这也是上述中间件为什么能够通过字符串的方式导入模块的关键所在
import importlib
res = 'myfile.b'
ret = importlib.import_module(res) # from myfile import b
# 该方法最小只能到py文件名
print(ret) # 结果是一个对象
核心代码:
import settings
import importlib
def send_all(content):
# 获取配置文件中的模块路径和类名
for path_str in settings.NOTIFY_LIST: # 'notify.email.Email'
module_path,class_name = path_str.rsplit('.',maxsplit=1)
# module_path = 'notify.email' class_name = 'Email'
# 1 利用字符串导入模块
module = importlib.import_module(module_path) # from notify import email
# 2 利用反射获取类名
cls = getattr(module,class_name) # Email、QQ、Wechat
# 3 生成类的对象
obj = cls()
# 4 利用鸭子类型直接调用send方法
obj.send(content)