Django 中间件

要点概论:

1. 中间件介绍

2. 中间件的执行流程

3. 自定义中间件

4. 中间件版登录验证

1. 中间件介绍

  官方说法:中间件时一个用来处理Django的请求和响应的框架级别的钩子。

  它是一个轻量,低级别的插件系统,用于在全局范围内改变 Django 的出入和输出。

  每个中间件组件都负责做一些特定的功能。

  打开Django项目的 Setting.py 文件,可以看到 MIDDLEWARE配置项:

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配置项是一个列表,列表中的字符串其实是一个个类,也就是一个个中间件。

2. 中间件的执行流程

  首先来看一下 Django 框架的请求流程图,了解一下中间件在请求流程中的作用:

  即在socket和路由系统之前,每次进行request(输入)和response(输出)的时候都要经过中间件。

  请求到达中间件之后,先按照正序执行每个注册中间件的 process_reques方法,如果返回None,

  就依次执行后续中间件,如果返回HttpResponse对象,就不再执行后面的process_request方法,

  而是执行当前对应中间件的process_response方法,将HttpResponse对象返回给浏览器。

  如下图所示:

process_request方法都执行完后,匹配路由,找到要执行的视图函数,先不执行视图函数,先执行中间件中的process_view方法,process_view方法返回None,继续按顺序执行,所有process_view方法执行完后执行视图函数。加入中间件3 的process_view方法返回了HttpResponse对象,则4,5,6的process_view以及视图函数都不执行,直接从最后一个中间件,也就是中间件6的process_response方法开始倒序执行:

process_template_response和process_exception两个方法的触发是有条件的,执行顺序也是倒序。总结所有的执行流程如下:

3. 自定义中间件

  中间件可以定义五个方法(其中主要的是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)

  以上方法的返回值可以hi是None或一个HttpResponse对象,如果是None,则继续按照django流程

  执行后续中间件,如果是HttpResponse对象,则直接将该对象返回给用户。

class MD1(MiddlewareMixin):
    # 处理请求的方法
    def process_request(self,request):
        print('这是中间件1号的请求')
        print('中间件1号请求的内存地址【{}】'.format(id(request)))
        
        request.one = '1号请求设置的属性'
        
    # 处理响应的方法
    def process_response(self,request,response):
        print('中间件1号里面的处理响应方法')
        return response
    
    
    # 中间件处理视图的方法
    def process_view(self, request, view_func, view_args, view_kwargs):
        '''
        :param request: 请求对象
        :param view_func:  将要执行的视图函数
        :param view_args:  将要执行的视图函数的位置参数
        :param view_kwargs:  将要执行的视图函数的关键字参数
        :return: 
        '''
    
        print('中间件1号的视图方法')
    
    # 中间件处理异常的方法
    def process_exception(self,request,exception):
        print('中间件一号的异常处理方法')
        
    # 中间件中处理模板响应
    def process_template_response(self, request, response):
        print("中间件1号中的模板响应方法")
        return response
    

class MD2(MiddlewareMixin):
    # 处理请求方法
    def process_request(self, request):
        print("这是中间件2号里面的请求方法")
        print('中间件2号的请求内存地址【{}】'.format(id(request)))
        print(request.one)
        
    # 处理响应方法
    def process_response(self, request, response):
        print("中间件2号里面的处理响应方法")
        return response

    # 处理视图方法
    def process_view(self, request, view_func, view_args, view_kwargs):
        """
        :param request: 请求对象
        :param view_func: 将要执行的视图函数
        :param view_args: 将要执行的视图函数的位置参数
        :param view_kwargs: 将要执行的视图函数的关键字参数
        :return:
        """
        print("中间件2号中的处理视图方法")
        # print(type(view_func))
        # print(view_func.__doc__)
        # print(view_func.__name__)
    
    # 处理异常方法
    def process_exception(self, request, exception):
        """
        中间件中处理异常的方法
        :param request:
        :param exception: 视图函数中抛出的异常
        :return:
        """
        print("*" * 120)
        print(exception)
        print("*" * 120)
        print("中间件2号中的处理异常方法")
  
    # 处理模板响应
    def process_template_response(self, request, response):
        print("中间件2号中的处理模板响应")
        return response
自定义中间件示例
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',
    'middlewares.MD1',  # 自定义中间件MD1
    'middlewares.MD2'  # 自定义中间件MD2
]
配置项中注册中间件
这是中间件1号的请求
[10/Apr/2018 17:13:31] "GET /index/ HTTP/1.1" 200 21
中间件1号请求的内存地址【1261599124112】
这是中间件2号里面的请求方法
中间件2号的请求内存地址【1261599124112】
1号请求设置的属性
中间件1号的视图方法
中间件2号中的处理视图方法
中间件2号里面的处理响应方法
中间件1号里面的处理响应方法
访问视图时中间件执行顺序

   总结:

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

  2)当配置多个中间件时,会按照MIDDLEWARE中的注册顺序,也就是列表的索引值,从前到后依次执行的。

  3)不同中间件之间传递的request都是同一个对象

  多个中间件中的process_response方法是按照MIDDLEWARE中的注册顺序倒序执行的,

  也就是说第一个中间件的process_request方法首先执行,而它的process_response方法最后执行,

  最后一个中间件的process_request方法最后一个执行,它的process_response方法是最先执行。

 

 4. 中间件版登录验证

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^index/$', views.index),
    url(r'^login/$', views.login, name='login'),
]
urls.py
from django.shortcuts import render, HttpResponse, redirect


def index(request):
    return HttpResponse('this is index')


def home(request):
    return HttpResponse('this is home')


def login(request):
    if request.method == "POST":
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")

        if user == "Q1mi" and pwd == "123456":
            # 设置session
            request.session["user"] = user
            # 获取跳到登陆页面之前的URL
            next_url = request.GET.get("next")
            # 如果有,就跳转回登陆之前的URL
            if next_url:
                return redirect(next_url)
            # 否则默认跳转到index页面
            else:
                return redirect("/index/")
    return render(request, "login.html")
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>登录页面</title>
</head>
<body>
<form action="{% url 'login' %}">
    <p>
        <label for="user">用户名:</label>
        <input type="text" name="user" id="user">
    </p>
    <p>
        <label for="pwd">密 码:</label>
        <input type="text" name="pwd" id="pwd">
    </p>
    <input type="submit" value="登录">
</form>
</body>
</html>
login.html
lass AuthMD(MiddlewareMixin):
    white_list = ['/login/', ]  # 白名单
    balck_list = ['/black/', ]  # 黑名单

    def process_request(self, request):
        from django.shortcuts import redirect, HttpResponse

        next_url = request.path_info
        print(request.path_info, request.get_full_path())

        if next_url in self.white_list or request.session.get("user"):
            return
        elif next_url in self.balck_list:
            return HttpResponse('This is an illegal URL')
        else:
            return redirect("/login/?next={}".format(next_url))
middlewares.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',
    'middlewares.AuthMD',
]
在settings.py中注册

AuthMD中间件注册后,所有的请求都要走AuthMD的process_request方法。

访问的URL在白名单内或者session中有user用户名,则不做阻拦走正常流程;

如果URL在黑名单中,则返回This is an illegal URL的字符串;

正常的URL但是需要登录后访问,让浏览器跳转到登录页面。

注:AuthMD中间件中需要session,所以AuthMD注册的位置要在session中间的下方。

  

原文地址:https://www.cnblogs.com/HZY258/p/8779868.html