django 中间件

简介

中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。每个中间件组件都负责做一些特定的功能。
但是由于其影响的是全局,所以需要谨慎使用,使用不当会影响性能。
说的直白一点中间件是帮助我们在视图函数执行之前和执行之后都可以做一些额外的操作,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在请求的特定的时间去执行这些方法


# setttings.py
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',
]

# MIDDLEWARE配置项是一个列表,列表中是一个个字符串,这些字符串其实是一个个类,也就是一个个中间件。

自定义中间件

中间件可以定义五个方法,分别是:(主要的是process_request和process_response)

  • 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)

以上方法的返回值可以是None或一个HttpResponse对象,如果是None,则继续按照django定义的规则向后继续执行,如果是HttpResponse对象,则直接将该对象返回给用户。

如何自定义中间件

1.在项目名或者应用名下创建一个任意名称的文件夹
2.在该文件夹内创建一个任意名称的py文件
3.在该py文件内需要书写类(这个类必须继承MiddlewareMixin)
	然后在这个类里面就可以自定义五个方法了
	(这五个方法并不是全部都需要书写,用几个写几个)
4.需要将类的路径以字符串的形式注册到配置文件中才能生效
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',
    '你自己写的中间件的路径1',
    '你自己写的中间件的路径2',
    '你自己写的中间件的路径3',
]

process_request(用来做全局相关的所有限制功能)

process_request有一个参数,就是request,这个request和视图函数中的request是一样的(在交给Django后面的路由之前,对这个request对象可以进行一系列的操作)。
由于request对象是一样的,所以我们可以对request对象进行一系列的操作,包括request.变量名=变量值,这样的操作,我们可以在后续的视图函数中通过相同的方式即可获取到我们在中间件中设置的值。

它的返回值可以是None也可以是HttpResponse对象。返回值是None的话,按正常流程继续走,交给下一个中间件处理,如果是HttpResponse对象,Django将不执行视图函数,而将相应对象返回给浏览器

# /app01/mymiddleware/mydd.py  自定义的中间件
from django.utils.deprecation import MiddlewareMixin


class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1里面的 process_request")


class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2里面的 process_request")


# settings.py
MIDDLEWARE = [
    ...
    # 注册自己的中间件(在应用下创建路径有提示,但是如果在项目下创建就没有提示了 你就需要自己比对着书写)
    'app01.mymiddleware.mydd.MD1', # 自定义中间件MD1
    'app01.mymiddleware.mydd.MD2' # 自定义中间件MD2
]

# 此时,我们访问一个视图,会发现终端中打印如下内容:

MD1里面的 process_request
MD2里面的 process_request
...


"""
1.中间件的process_request方法是在执行视图函数之前执行的。

2.请求来的时候需要经过每一个中间件里面的process_request方法,结果的顺序是按照配置文件中注册的中间件从上往下的顺序依次执行

3.如果中间件里面没有定义该方法,那么直接跳过执行下一个中间件

4.如果该方法返回了HttpResponse对象,那么请求将不再继续往后执行而是直接原路返回(校验失败不允许访问...)

5.不同中间件之间传递的request都是同一个对象
"""

process_response

多个中间件中的process_response方法是按照MIDDLEWARE中的注册顺序倒序执行的,也就是说第一个中间件的process_request方法首先执行,而它的process_response方法最后执行,最后一个中间件的process_request方法最后一个执行,它的process_response方法是最先执行。
定义process_response方法时,必须给方法传入两个形参,request和response。request就是上述例子中一样的对象,response是视图函数返回的HttpResponse对象(也就是说这是Django后台处理完之后给出一个的一个具体的视图)。该方法的返回值(必须要有返回值)也必须是HttpResponse对象。如果不返回response而返回其他对象,则浏览器不会拿到Django后台给他的视图,而是我的中间件中返回的对象


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


MIDDLEWARE = [
    ...
    'app01.mymiddleware.mydd.MD2',
    'app01.mymiddleware.mydd.MD1'
]


# 访问一个视图,输出如下:
MD2里面的 process_request
MD1里面的 process_request
视图输出
MD1里面的 process_response
MD2里面的 process_response


"""
1.响应走的时候需要经过每一个中间件里面的process_response方法,该方法有两个额外的参数request,response

2.该方法必须返回一个HttpResponse对象
	(1).默认返回的就是形参response
	(2).你也可以自己返回自己写的对象,但这样浏览器不会拿到Django后台给他的视图,而是我们返回的对象

3.执行顺序是按照配置文件中注册了的中间件从下往上依次执行,如果你没有定义的话直接跳过执行下一个

4.如果在第一个process_request方法就已经返回了HttpResponse对象,那么响应走的时候就是会直接走同级别的process_reponse返回,不会经过所有的中间件里面的process_response
"""

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方法,最后返回给浏览器


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

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD1 中的process_view")
        print(view_func, view_func.__name__)


class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2里面的 process_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("-" * 80)
        print("MD2 中的process_view")
        print(view_func, view_func.__name__)

MIDDLEWARE = [
    ...
    'app01.mymiddleware.mydd.MD2',
    'app01.mymiddleware.mydd.MD1'
]

# 访问index视图函数 输出:
MD2里面的 process_request
MD1里面的 process_request
--------------------------------------------------------------------------------
MD2 中的process_view
<function index at 0x000001DE68317488> index
--------------------------------------------------------------------------------
MD1 中的process_view
<function index at 0x000001DE68317488> index
app01 中的 index视图
MD1里面的 process_response
MD2里面的 process_response


"""
process_view方法是在Django路由系统之后,视图系统之前执行的,执行顺序按照MIDDLEWARE中的注册顺序从上到下顺序执行的
"""

process_template_response

process_template_response(self, request, response)
它的参数,一个HttpRequest对象,response是TemplateResponse对象(由视图函数或者中间件产生)。
process_template_response是在视图函数执行完成后立即执行,但是它有一个前提条件,那就是视图函数返回的对象有一个render()方法(或者表明该对象是一个TemplateResponse对象或等价方法)。

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("-" * 80)
        print("MD1 中的process_view")
        print(view_func, view_func.__name__)


    def process_template_response(self, request, response):
        print("MD1 中的process_template_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

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD2 中的process_view")
        print(view_func, view_func.__name__)

    def process_template_response(self, request, response):
        print("MD2 中的process_template_response")
        return response


MIDDLEWARE = [
    ...
    'app01.mymiddleware.mydd.MD2',
    'app01.mymiddleware.mydd.MD1'
]

# views.py
def index(request):
    print("app01 中的 index视图")

    def render():
        print("in index/render")
        return HttpResponse("O98K")
    rep = HttpResponse("OK")
    rep.render = render
    return rep

# 访问index视图
MD2里面的 process_request
MD1里面的 process_request
--------------------------------------------------------------------------------
MD2 中的process_view
<function index at 0x000001C111B97488> index
--------------------------------------------------------------------------------
MD1 中的process_view
<function index at 0x000001C111B97488> index
app01 中的 index视图
MD1 中的process_template_response
MD2 中的process_template_response
in index/render
MD1里面的 process_response
MD2里面的 process_response


"""
返回的HttpResponse对象有render属性的时候才会触发,顺序是按照配置文件中注册了的中间件从下往上依次经过
"""

process_exception

process_exception(self, request, exception)
该方法两个参数:
一个HttpRequest对象
一个exception是视图函数异常产生的Exception对象。
这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。

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

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD1 中的process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD1 中的process_exception")
        return HttpResponse(str(exception))  # 返回一个响应对象

class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2里面的 process_request")
        pass

    def process_response(self, request, response):
        print("MD2里面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD2 中的process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD2 中的process_exception")

# 如果视图函数中无异常,process_exception方法不执行。

# views.py
def index(request):
    print("app01 中的 index视图")
    raise ValueError("呵呵") # 手动抛出异常
    return HttpResponse("O98K")


MIDDLEWARE = [
    ...
    'app01.mymiddleware.mydd.MD2',
    'app01.mymiddleware.mydd.MD1'
]


# 输出
MD2里面的 process_request
MD1里面的 process_request
--------------------------------------------------------------------------------
MD2 中的process_view
<function index at 0x0000022C09727488> index
--------------------------------------------------------------------------------
MD1 中的process_view
<function index at 0x0000022C09727488> index
app01 中的 index视图
呵呵
MD1 中的process_exception
MD1里面的 process_response
MD2里面的 process_response

# 注意,这里并没有执行MD2的process_exception方法,因为MD1中的process_exception方法直接返回了一个响应对象。


"""
当视图函数中出现异常的情况下触发,顺序是按照配置文件中注册了的中间件从下往上依次经过
"""

扩展

请求到达中间件之后,先按照正序执行每个注册中间件的process_request方法,process_request方法返回的值是None,就依次执行,如果返回的值是HttpResponse对象,不再执行后面的process_request方法,而是执行当前对应中间件的process_response方法(注意不是掉头执行所有的process_response方法),将HttpResponse对象返回给浏览器。也就是说:如果MIDDLEWARE中注册了6个中间件,执行过程中,第3个中间件返回了一个HttpResponse对象,那么第4,5,6中间件的process_request和process_response方法都不执行,顺序执行3,2,1中间件的process_response方法。

csrf跨站请求伪造

网站在给用户返回一个具有提交数据功能页面的时候会给这个页面加一个唯一标识,当这个页面朝后端发送post请求的时候 我的后端会先校验唯一标识,如果唯一标识不对直接拒绝(403 forbbiden)如果成功则正常执行

<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>

AJAX请求如何设置csrf_token


1.方式一

$.ajax({
  url: "/cookie_ajax/",
  type: "POST",
  data: {
    "username": "Tonny",
    "password": 123456,
    "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val()  // 使用JQuery取出csrfmiddlewaretoken的值,拼接到data中
  },
  success: function (data) {
    console.log(data);
  }
})


2.方式二
$.ajax({
  url: "/cookie_ajax/",
  type: "POST",
  data: {
    "username": "Tonny",
    "password": 123456,
    "csrfmiddlewaretoken": '{{ csrf_token }}'  // 利用模版语法提供的快捷书写,拼接到data中
  },
  success: function (data) {
    console.log(data);
  }
})


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);
    }
  }
});

csrf相关装饰器

"""
csrf_protect 为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
csrf_exempt 取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
"""
from django.views.decorators.csrf import csrf_protect,csrf_exempt

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
from django.utils.decorators import method_decorator

# @method_decorator(csrf_protect,name='post')  # 针对csrf_protect 第二种方式可以
# @method_decorator(csrf_exempt,name='post')  # 针对csrf_exempt 第二种方式不可以
# @method_decorator(csrf_exempt, name='dispatch') # 针对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')
posted @ 2020-06-08 15:54  风亦缘^_^  阅读(87)  评论(0编辑  收藏  举报