Django 中间件

一、什么是中间件

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

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

二、自定义中间件

中间件可以定义五个方法

process_request(self,request)
process_view(self, request, view_func, view_args, view_kwargs)
process_exception(self, request, exception)
process_template_response(self,request,response)
process_response(self, request, response)

自己定义一个中间件,需要写一个类,这个类必须继承 MiddlewareMixin

# views.py

def index(request):
    print("views.py(视图函数)")
    return HttpResponse("ok")
# my_middleware.py

from django.utils.deprecation import MiddlewareMixin


class Test1Middleware(MiddlewareMixin):

    def process_request(self, request):
        print("Test1请求")

    def process_response(self, request, response):
        print("Test1返回")
        return response


class Test2Middleware(MiddlewareMixin):

    def process_request(self, request):
        print("Test2请求")

    def process_response(self, request, response):
        print("Test2返回")
        return response

结果:

Test1请求
Test2请求
views.py(视图函数)
Test2返回
Test1返回

注意:

如果当请求到达 Test2请求 的时候直接不符合条件返回,即 return HttpResponse("Test2Middleware中断"),程序将把请求直接发给中间件 Test2Middleware 返回,然后依次返回到请求者:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class Test1Middleware(MiddlewareMixin):
    ...

class Test2Middleware(MiddlewareMixin):

    def process_request(self, request):
        print("Test2请求")
        return HttpResponse("Test2Middleware中断")
    ...

结果:

Test1请求
Test2请求
Test2返回
Test1返回

process_request

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

process_response

该方法有两个参数,一个是request,一个是response,request就是上述例子中一样的对象,response是视图函数返回的HttpResponse对象。该方法的返回值也必须是HttpResponse对象。

process_view

该方法有四个参数;

request      HttpRequest对象
view_func    Django即将使用的视图函数(它是实际的函数对象,而不是函数的名称作为字符串)
view_args    将传递给视图的位置参数的列表
view_kwargs  将传递给视图的关键字参数的字典;view_args和view_kwargs都不包含第一个视图参数(request)

class Test1Middleware(MiddlewareMixin):

    def process_request(self, request):
        print("Test1请求")

    def process_response(self, request, response):
        print("Test1返回")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("Test1_view")


class Test2Middleware(MiddlewareMixin):

    def process_request(self, request):
        print("Test2请求")

    def process_response(self, request, response):
        print("Test2返回")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("Test2_view")

结果:

Test1请求
Test2请求
Test1_view
Test2_view
views.py(视图函数)
Test2返回
Test1返回

process_exception

该方法两个参数;

request      HttpRequest对象
exception    视图函数异常产生的Exception对象

这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。

process_template_response(用的比较少)

该方法两个参数;

request      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_exception(self, request, exception):
        print(exception)
        print("MD1 中的process_exception")
        return HttpResponse(str(exception))

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

    def process_template_response(self, request, response):
        print("MD2 中的process_template_response")
        return response
View Code
def index(request):
    print("app01 中的 index视图")

    def render():
        print("in index/render")
        return HttpResponse("O98K")
    rep = HttpResponse("OK")
    rep.render = render
    return rep
views.py
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
结果

三、应用案例

①做IP访问频率限制

某些IP访问服务器的频率过高,进行拦截,比如限制每分钟不能超过20次。

②URL访问过滤

如果用户访问的是login视图(放行);

如果访问其他视图,需要检测是不是有session认证,已经有了放行,没有返回login,这样就省得在多个视图函数上写装饰器了!

中间件版登录校验

urls.py

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

urlpatterns = [
    url(r'^index/', views.index),
    url(r'^home/', views.home),
    url(r'^login/', views.login),
]
View Code

views.py

from django.shortcuts import HttpResponse
from django.shortcuts import redirect
from appxx import models


def login(request):
    if request.method == "POST":
        user = request.POST.get("username")
        pwd= request.POST.get("password")
        obj = models.UserInfo.objects.filter(username=user, password=pwd)
        if obj:
            request.session["user"] = user
            request.session.set_expiry(30)  # session会在30秒数后失效
            next_url = request.GET.get("next")
            if next_url:
                return redirect(next_url)
            else:
                return redirect("/home/")
    return render(request, "login.html")


def index(request):
    return HttpResponse("This's index.html.")


def home(request):
    return HttpResponse("This's home.html.")
View Code

my_middleware.py

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


class CheckLoginMiddleware(MiddlewareMixin):
    white_list = ["/login/"]
    black_list = ["/black/"]

    def process_request(self, request):
        next_nrl = request.path_info
        if next_nrl in self.white_list or request.session.get("user"):
            return  # 只写return,相当于结束一个函数的继续
        elif next_nrl in self.black_list:
            return HttpResponse("This's an illegal URL.")
        else:
            return redirect("/login/?next={}".format(next_nrl))
View Code

login.html

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="{{ request.get_full_path }}" method="post">
    {% csrf_token %}
    <p>
        <label for="username">账号:</label>
        <input type="text" id="username" name="username">
    </p>
    <p>
        <label for="password">密码:</label>
        <input type="password" id="password" name="password">
    </p>
    <p>
        <label for="submit"></label>
        <input type="submit" value="登录">
    </p>
</form>
</body>
</html>
View Code

CheckLoginMiddleware 中间件祖册后,所有的请求都要走 CheckLoginMiddleware 的 process_request 方法;访问的URL在白名单内或者 session 中有 xxx 用户名,则不做阻拦,走正常流程;如果URL在黑名单中,则返回 "This's an illegal URL." 的字符串;其他正常的URL如果需要登录后才能访问,则让浏览器跳转到登录页面。

原文地址:https://www.cnblogs.com/believepd/p/9940105.html