Django的中间件2-中间件的执行流程及自定义中间件的几个实例

Django的请求流程

中间件的执行流程

1、请求到达中间件之后,先按照正序执行每个注册中间件的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方法。)

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

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

 

简单的session认证

注意~如果是基于session的认证,需要写在Django自带的session中间件的下面!

class M1(MiddlewareMixin):

    def process_request(self,request):

        #设置路径白名单,只要访问的是login登陆路径,就不做这个cookie认证
        if request.path not in [reverse('login'),]:
            print('我是M1中间件') #客户端IP地址
            # return HttpResponse('sorry,没有通过我的M1中间件')
            is_login = request.COOKIES.get('is_login', False)

            if is_login:
                pass
            else:
                # return render(request,'login.html')
                return redirect(reverse('login'))
        else:
            return None #别忘了return None,或者直接写个pass

    def process_response(self,request,response):

        print('M1响应部分')
        # print(response.__dict__['_container'][0].decode('utf-8'))
        return response
        # return HttpResponse('认证失败')

中间件版登陆认证程序--用到白名单

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

路由

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

urlpatterns = [
    url(r'^index/$', views.index),
    url(r'^login/$', views.login, name='login'),
]
路由

视图函数

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")
视图函数

login页面

<!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' %}" method="post">
    <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页面

自定义中间件的内容

自定义中间件中middlewares.py的内容如下:

class 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()) #注意path_info与get_full_path()的区别 if next_url in self.white_list or request.session.get("user"): return None elif next_url in self.balck_list:
#如果在黑名单的路径
return HttpResponse('This is an illegal URL') else: #登陆成功后跳转到用户想要去的那个页面 return redirect("/login/?next={}".format(next_url))

在settings.py中注册

我在项目中新建了一个core包,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',
    'core.middlewares.AuthMD',
]

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

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

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

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

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

DRF中的频率组件

可以限制用户访问的频率

在throttles.py中

from rest_framework.throttling import BaseThrottle,SimpleRateThrottle
import time
from rest_framework import exceptions
visit_record = {}
class VisitThrottle(BaseThrottle):
    # 限制访问时间
    VISIT_TIME = 10
    VISIT_COUNT = 3

    # 定义方法 方法名和参数不能变
    def allow_request(self, request, view):
        # 获取登录主机的id
        id = request.META.get('REMOTE_ADDR')
        self.now = time.time()

        if id not in visit_record:
            visit_record[id] = []

        self.history = visit_record[id]
        # 限制访问时间
        while self.history and self.now - self.history[-1] > self.VISIT_TIME:
            self.history.pop()
        # 此时 history中只保存了最近10秒钟的访问记录
        if len(self.history) >= self.VISIT_COUNT:
            return False
        else:
            self.history.insert(0, self.now)
            return True

    def wait(self):
        return self.history[-1] + self.VISIT_TIME - self.now

在views.py中

from app01.service.throttles import *

class BookViewSet(generics.ListCreateAPIView):
    throttle_classes = [VisitThrottle,]
    queryset = Book.objects.all()
    serializer_class = BookSerializers

全局视图throttle

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
    "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",],
    "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",]
}

内置throttle类

在throttles.py修改为:  

class VisitThrottle(SimpleRateThrottle):

    scope="visit_rate"
    def get_cache_key(self, request, view):

        return self.get_ident(request)

settings.py设置

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
    "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",],
    "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",],
    "DEFAULT_THROTTLE_RATES":{
        "visit_rate":"5/m",
    }
}

 

posted on 2019-06-06 20:35  江湖乄夜雨  阅读(561)  评论(0编辑  收藏  举报