django之中间件
目录
django之中间件
一 中间件简介
# 中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能
# django内置中间件
	'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',
    # Django默认的中间件:
-在django项目的settings模块中,有一个 MIDDLEWARE_CLASSES 变量,其中每一个元素就是一个中间件
# Django中间件的定义:
Middleware is a framework of hooks into Django’s request/response processing. 
It’s a light, low-level “plugin” system for globally altering Django’s input or output.
二 自定义中间件
1 自定义步骤:
1)-写一个类,继承MiddlewareMixin
2)-里面写方法process_request(请求来了,一定会触发它的执行)
3)-在setting中配置(注意,放在前和放在后)
MIDDLEWARE = [
    ...
    'app01.mymiddle.MyMiddleware1',
    ...
]
MiddlewareMixin类
# MiddlewareMixin类
# from django.utils.deprecation import MiddlewareMixin
class MiddlewareMixin:
    def __init__(self, get_response=None):
        self.get_response = get_response
        super().__init__()
    def __call__(self, request):
        response = None
        if hasattr(self, 'process_request'):
            response = self.process_request(request)
        if not response:
            response = self.get_response(request)
        if hasattr(self, 'process_response'):
            response = self.process_response(request, response)
        return response
2 中间件主要方法:
# 1) 中间件中主要有几个方法:
	process_request(self,request)
	process_response(self, request, response)
----------------------------------------------
	process_view(self, request, callback, callback_args, callback_kwargs)
	process_exception(self, request, exception)
----------------------------------------------
	process_template_response(self,request,response)
3 process_request和process_response
0) 是使用最多的两种方法!!
1) process_request(request对象)
2) process_response(request对象,response对象)
3) 多个中间件,执行顺序是什么?
	-请求来的时候,从上往下执行:process_request
    -请求走的时候,从下往上执行:process_response
4) process_request可以干什么?【应用场景】
	-写一个中间件,不管前端用什么编码,在requset.data中都有post的数据
    -频率限制(限制某个ip地址,一分钟只能访问5次)
    -登录认证(只要没登录,重定向到login路径)、
    -记录用户访问日志(ip,时间,访问路径)
    
5) process_response可以干什么?内部有response对象【应用场景】
	-统一给所有(某几个路径)加cookie
    -统一给所有(某几个路径)加响应头
6) process_request和process_response执行流程:
	-当用户发起请求的时候会依次经过所有的的中间件,这个时候的请求是process_request, 最后到达views的函数中,views函数处理后,再依次穿过中间件,这个时候是process_response,最后返回给请求者。

上述截图中的中间件都是django中的,我们也可以自己定义一个中间件,我们可以自己写一个类,但是必须继承MiddlewareMixin。
7) 自定义中间件的详细执行流程
-7.1) 第一步:导入
	-from django.utils.deprecation import MiddlewareMixin
-7.2) 第二步:自定义中间件代码
from django.utils.deprecation import MiddlewareMixin
# process_request
# process_response
from django.shortcuts import render, HttpResponse
class MyMiddleware1(MiddlewareMixin):
    # 所有的请求来了,都会走到它
    def process_request(self, request):
        print('请求来了1')
        # print(request.session)
        # 可不可以返回?必须返回HttpResponse的对象
        # return HttpResponse('我不让你访问')  # 执行到这里就直接返回,后续的代码不再执行
    # 所有的请求走了,都会执行它
    def process_response(self, request, response):
        print('请求走了1')
        # response['name'] = 'lxx'
        # print(response)  # <HttpResponse status_code=200, "text/html; charset=utf-8">
        return response  # 此处必须return这个response,不写的话默认返回None,后续中间件会用到response,找不到就会报错
class MyMiddleware2(MiddlewareMixin):
    # 所有的请求来了,都会走到它
    def process_request(self, request):
        print('请求来了2')
        # return HttpResponse('dsb, 视图函数不执行了吧')
    # 所有的请求走了,都会执行它
    def process_response(self, request, response):
        print('请求走了2')
        return response
-7.3) 在view中定义一个视图函数(index)
def index(request):
    print('我是视图函数')
    return HttpResponse('ok')
-7.4) 第四步:在settings.py的MIDDLEWARE里注册自己定义的中间件

# 执行结果
"""请求来了1
请求来了2
我是视图函数
请求走了2
请求走了1"""
8) 注意:
-如果当请求到达请求2的时候直接不符合条件返回,即return HttpResponse("Md2中断"),程序将把请求直接发给中间件2返回,然后依次返回到请求者,结果如下:
-返回Md2中断的页面,后台打印如下:
"""
Md1请求
Md2请求
Md2返回
Md1返回"""
流程图如下:

9) 总结:
-中间件的process_request方法是在执行视图函数之前执行的。
-当配置多个中间件时,会按照MIDDLEWARE中的注册顺序,也就是列表的索引值,从前到后依次执行的。
-不同中间件之间传递的request都是同一个对象
-多个中间件中的process_response方法是按照MIDDLEWARE中的注册顺序倒序执行的,也就是说第一个中间件的process_request方法首先执行,而它的process_response方法最后执行,最后一个中间件的process_request方法最后一个执行,它的process_response方法是最先执行。
4 process_view
-Django会在路由匹配成功,调用视图函数之前调用process_view方法。
def process_view(self, request, callback, callback_args, callback_kwargs):
# 该方法有四个参数
    -request		# 是HttpRequest对象。
    -callback 		# 是Django即将使用的视图函数。 (它是实际的函数对象,而不是函数的名称作为字符串。)
    -callback_args 	# 是将传递给视图的位置参数的列表.
    -callback_kwargs # 是将传递给视图的关键字参数的字典。 view_args和view_kwargs都不包含第一个视图参数(request)。
-它应该返回None或一个HttpResponse对象。 如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个HttpResponse对象,Django不会调用适当的视图函数。 它将执行中间件的process_response方法并将应用到该HttpResponse并返回结果。
process_view正常的执行:
# process_view正常的执行:
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, HttpResponse
class MyMiddleware1(MiddlewareMixin):
    # 所有的请求来了,都会走到它
    def process_request(self, request):
        print('请求来了1')
        # print(request.session)
        # 可不可以返回?必须返回HttpResponse的对象
        # return HttpResponse('我不让你访问')  # 执行到这里就直接返回,后续的代码不再执行
    # 所有的请求走了,都会执行它
    def process_response(self, request, response):
        print('请求走了1')
        # response['name'] = 'lxx'
        # print(response)  # <HttpResponse status_code=200, "text/html; charset=utf-8">
        return response  # 此处必须return这个response,不写的话默认返回None,后续中间件会用到response,找不到就会报错
    def process_view(self, request, callback, callback_args, callback_kwargs):
        print('中间件1的process_view')
class MyMiddleware2(MiddlewareMixin):
    # 所有的请求来了,都会走到它
    def process_request(self, request):
        print('请求来了2')
        # return HttpResponse('dsb, 视图函数不执行了吧')
    # 所有的请求走了,都会执行它
    def process_response(self, request, response):
        print('请求走了2')
        return response
# 执行结果:
"""请求来了1
请求来了2
中间件1的process_view
我是视图函数
请求走了2
请求走了1"""
# 执行流程图:

# process_view可以用来调用视图函数:
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, HttpResponse
class MyMiddleware1(MiddlewareMixin):
    # 所有的请求来了,都会走到它
    def process_request(self, request):
        print('请求来了1')
        # print(request.session)
        # 可不可以返回?必须返回HttpResponse的对象
        # return HttpResponse('我不让你访问')  # 执行到这里就直接返回,后续的代码不再执行
    # 所有的请求走了,都会执行它
    def process_response(self, request, response):
        print('请求走了1')
        # response['name'] = 'lxx'
        # print(response)  # <HttpResponse status_code=200, "text/html; charset=utf-8">
        return response  # 此处必须return这个response,不写的话默认返回None,后续中间件会用到response,找不到就会报错
    def process_view(self, request, callback, callback_args, callback_kwargs):
        res = callback(request)
        print('中间件1的process_view')
        return res
# 执行结果:
"""请求来了1
请求来了2
我是视图函数
中间件1的process_view
请求走了2
请求走了1"""
# 注意:
-process_view如果有返回值,会越过其他的process_view以及视图函数,但是所有的process_response都还会执行。
5 process_exception
-视图函数出错,紧后会执行它(全局异常捕获)("记录日志,哪个ip地址,访问哪个路径,出的错")# 全局异常捕获,返回4开头的
def process_exception(self, request, exception):
# 该方法两个参数:
    -一个HttpRequest对象
    -一个exception是视图函数异常产生的Exception对象。
	-这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。
process_exception方法来处理异常:
# process_exception方法来处理异常:
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, HttpResponse
class MyMiddleware1(MiddlewareMixin):
    # 所有的请求来了,都会走到它
    def process_request(self, request):
        print('请求来了1')
        # print(request.session)
        # 可不可以返回?必须返回HttpResponse的对象
        # return HttpResponse('我不让你访问')  # 执行到这里就直接返回,后续的代码不再执行
    # 所有的请求走了,都会执行它
    def process_response(self, request, response):
        print('请求走了1')
        # response['name'] = 'lxx'
        # print(response)  # <HttpResponse status_code=200, "text/html; charset=utf-8">
        return response  # 此处必须return这个response,不写的话默认返回None,后续中间件会用到response,找不到就会报错
    def process_view(self, request, callback, callback_args, callback_kwargs):
        # res = callback(request)
        print('中间件1的process_view')
        # return res
class MyMiddleware2(MiddlewareMixin):
    # 所有的请求来了,都会走到它
    def process_request(self, request):
        print('请求来了2')
        # return HttpResponse('dsb, 视图函数不执行了吧')
    # 所有的请求走了,都会执行它
    def process_response(self, request, response):
        print('请求走了2')
        return response
    # 全局异常捕获,返回4开头的
    def process_exception(self, request, exception):
        print(exception)
        print('exception')
        return HttpResponse('error')
        # return render(request, 'error.html')
[views.py]:
from django.shortcuts import render, HttpResponse
def index(request):
    print('我是视图函数')
    a
    return HttpResponse('ok')
    # return render(request, 'index.html')
# 执行结果:
"""请求来了1
请求来了2
中间件1的process_view
我是视图函数
name 'a' is not defined
exception
请求走了2
请求走了1"""
# 流程图如下:

# 总结:
	-以视图函数为中间点,视图函数之前的 process_request和 process_view都是"自上至下"执行,
    -视图函数之后的 process_exception和 process_response都是"自下至上"执行。
6 process_template_response
-使用较少:
-该方法对视图函数返回值有要求,必须是一个含有render方法类的对象,才会执行此方法
三 CSRF_TOKEN跨站请求伪造
1 什么是跨站请求伪造
# 攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的。
2 django中间件解决csrf攻击
# 中间件的使用:django.middleware.csrf.CsrfViewMiddleware
	django中间件会浏览器在发送post请求的时候,给请求加上一个k:v形式的csrf_token字符串返回浏览器,并且有一个过期时间,下次再请求的时候需要验证这个字符串,没有字符串的请求就会拒绝访问。
- form表单提交
# 1) form表单提交
	-在form表单中 {% csrf_token %}
# 视图函数
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('登录成功!')
HTML
# HTML
<form action="" method="post">
    {% csrf_token %}
    <p>用户名:<input type="text" name="name"></p>
    <p>密码:<input type="password" name="password"></p>
    <p><input type="submit" value="提交"></p>
</form>
- ajax提交
# 2) ajax提交
# html
{% csrf_token %}
<p>用户名:<input type="text" name="name"></p>
<p>密码:<input type="password" name="password"></p>
<p><input type="submit" value="提交" id="submit"></p>
使用示例
// 方式一:放到data中
    $('#submit').click(function () {
        $.ajax({
            url: '/app01/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('lxxxxxxxxx')
                console.log(data)
            }
        })
    })
// 方式二:放到data中
	'csrfmiddlewaretoken':'{{ csrf_token }}'
// 方式三:放到请求头中
    $('#submit').click(function () {
        $.ajax({
            url: '/app01/csrf_test/',
            method: 'post',
            headers:{'X-CSRFToken':'{{csrf_token}}'},
            data: {'name': $('[name = "name"]').val(),
                'password': $('[name = "password"]').val(),
                // 'csrfmiddlewaretoken': $('[name = "csrfmiddlewaretoken"]').val(),  //必须加,不然后403forbidden
                // 'csrfmiddlewaretoken': '{{ csrf_token }}', //必须引号,否则就不是字符串了
            },
            success: function (data) {
                console.log('成功了')
                console.log(data)
            },
            error: function (data) {
                console.log('lxxxxxxxxx')
                console.log(data)
            }
        })
    })
- 使用jquery.cookie.js
# 3) 使用jquery.cookie.js
	-从cookie中取,则网页中的{% csrf_token %}就不需要了
	-在浏览器中对cookie进行增,删,查,改
    -前后端分离(js操作cookie)
3 csrf的局部使用、局部禁用
# 如何局部使用/禁用csrf
	-在视图函数上加装饰器
    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_token本可以正常使用,加上这个装饰器,就必须带上csrf_token才能使用了)
@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中
# 古怪的使用方式,在urls.py中
# 装饰器的原理就是把被装饰的函数当参数传入,这个方式直接在路由里给函数装饰
path('csrf_test/', csrf_exempt(views.csrf_test))
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号