Django中间件

一、什么是中间件

官方的说法:中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。每个中间件组件都负责做一些特定的功能。

但是由于其影响的是全局,所以需要谨慎使用,使用不当会影响性能。

 说的直白一点中间件是帮助我们在视图函数执行之前和执行之后都可以做一些额外的操作,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在处理请求的特定的时间去执行这些方法。

打开Django项目的Settings.py文件,看到下图的MIDDLEWARE配置项。

在Django中就是一个类,在全局范围内改变Django的输入和输出。

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)
    • 执行时间:在视图函数执行之前
    • 参数:request —— 跟视图函数中的是同一个

    • 执行顺序:按照注册顺序 顺序执行

    • 返回值:

      •   None: 正常流程

      •   HttpResponse对象:不执行后面中间中的process_request方法,不执行视图函数,直接执行当前中间件中的process_response方法,后面正常走

  • process_view(self, request, view_func, view_args, view_kwargs)
    • 执行时间:在process_request方法之后,在视图函数执行之前
    • 参数:
      • request —— 跟视图函数中的是同一个
      • view_func —— 视图函数
      • view_args —— 视图函数的位置参数
      • view_kwargs—— 视图函数的关键字参数
    • 执行顺序:按照注册顺序 顺序执行
    • 返回值:None: 正常流程   |  HttpResponse 对象
  • process_template_response(self,request,response)
    • 执行时间:视图函数返回的response对象有一个叫render的方法
    • 参数:request —— 跟视图函数中的是同一个
      • response —— 视图函数中传递的响应对象
    • 执行顺序:按照注册顺序 倒叙执行
    • 返回值:必须返回response对象
  • process_exception(self, request, exception)
    • 执行时间:出现异常的时候才执行
    • 参数:request —— 跟视图函数中的是同一个
      • exception—— 异常对象
    • 执行顺序:按照注册顺序 倒叙执行
    • 返回值:None: 正常流程
      • HttpResponse对象:不执行后面中间中的process_exception方法,直接执行最后一个中间件中的process_response方法,后面正常走
  • process_response(self, request, response)
    • 执行时间:在视图函数执行之后
      • 参数:request —— 跟视图函数中的是同一个   response —— 视图函数中传递的响应对象
      • 执行顺序:按照注册顺序 倒叙执行
      • 返回值:HttpResponse对象:必须是响应对象

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

自定义一个中间件示例

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

三、中间件的执行流程

请求到达中间件之后,先按照正序执行每个注册中间件的process_reques方法,process_request方法返回的值是None,就依次执行,如果返回的值是HttpResponse对象,不再执行后面的process_request方法,而是执行当前对应中间件的process_response方法,将HttpResponse对象返回给浏览器。

也就是说:如果MIDDLEWARE中注册了6个中间件,执行过程中,第3个中间件返回了一个HttpResponse对象,那么第4,5,6中间件的process_request和process_response方法都不执行,顺序执行3,2,1中间件的process_response方法。

总结所有的执行流程如下:

 

四、中间件实现登录验证和限制访问频率

中间件版的登录验证需要依靠session,所以数据库中要有django_session表。

urls.py

from django.contrib import admin
from django.urls import path, re_path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index),
    re_path('^$', views.index),
    path('home/', views.home, name="home"),
    path('login/', views.Login.as_view()),
    path('reg/', views.reg, name="reg"),
]
View Code

model.py

from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.


class UserInfo(AbstractUser):
    tel = models.CharField(max_length=32,null=True,blank=True)
    gender = models.IntegerField(choices=((1,""),(2,"")),default=1)
View Code

views.py

from django.shortcuts import render, HttpResponse, redirect
from django.http import JsonResponse

from django.views import View
# 引入Django用户认证组件
from django.contrib import auth

# Create your views here.

class Login(View):
    """
    登录逻辑
    """
    def get(self, request):

        return render(request, "login.html")

    def post(self, request):
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")
        # Ajax请求返回一个字典
        response = {"user": None, "err_msg": ""}
        # 用auth认证组件去数据库中验证用户名和密码是否正确
        user_obj = auth.authenticate(username=user, password=pwd)
        if user_obj:
            # 用户登录认证保存,给浏览器一个“钥匙”
            auth.login(request, user_obj)  # request.session["user_id"] = user_obj.pk
            response["user"] = user
        else:
            response["err_msg"] = "用户名或密码错误!"

        return JsonResponse(response)


def reg(request):
    """注册"""
    return HttpResponse("我是注册页面")


def index(request):
    """
    首页
    :param request:
    :return:
    """
    return HttpResponse("我是首页 Index")


def home(request):
    """主菜单"""
    return HttpResponse('我是home')
View Code

longin.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
    <link rel="icon" href="">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<h1>用户登录</h1>
<div class="container">
    <div class="row">
        <div class="col-md-4 col-md-offset-3">
            <form action="" method="post">
                {% csrf_token %}
                <div class="form-group">
                    <label for="">用户名</label>
                    <input type="text" class="form-control" id="user">
                </div>
                 <div class="form-group">
                    <label for="">密码</label>
                    <input type="password" class="form-control" id="pwd">
                </div>
                <input type="button" class="btn btn-primary pull-right login_btn" value="登录">
                <span class="error"></span>
            </form>
        </div>
    </div>
</div>

<script src="/static/js/jquery-3.3.js"></script>
<script>
     $(".login_btn").click(function () {

         $.ajax({
             url:"",
             type:"post",
             data:{
                 user:$("#user").val(),
                 pwd:$("#pwd").val(),
                 csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val()
             },
             success:function (response) {
                     console.log(response);

                     if(response.user){
                         // 登录成功
                         location.href="/"
                     }
                     else{
                         // 登录失败
                         $(".error").html(response.err_msg).css("color","red");
                         setTimeout(function () {
                             $(".error").html("")
                         },3000);
                     }
             }
         })


     });

</script>
</body>
</html>
View Code

my_middleware.py

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse, redirect
import time

class LoginMiddleWare(MiddlewareMixin):
    """登录认证中间件"""

    def process_request(self, request):

        # 获取到访问记录
        history = request.session.get('history', [])

        # 获取当前时间
        now = time.time()

        while history and now - history[-1] > 60:
            history.pop()

        if len(history) > 3:
            return HttpResponse('你的访问频率太快了,你歇一会')

        history.insert(0, now)
        request.session['history'] = history

        # 设置白名单
        if request.path in ['/login/', '/reg/']: # 白名单:
            return None

        # 没有携带session登录认证信息的不能访问,重定向到登录页面
        if not request.user.id:
            return redirect("/login/")
View Code

settings.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',
    'app01.middleware.my_middleware.LoginMiddleWare'
]

 

posted @ 2018-12-11 11:11  清风_Z  阅读(202)  评论(0编辑  收藏  举报