阿里云,服务器_Django 中间件

  • 阿里云,服务器_Django 中间件已关闭评论
  • 116 人浏览
  • A+
所属分类:首页

开端相识

 我们晓得在考证用户是不是上岸时,给视图函数加装潢器来推断是用户是不是登录,把没有登录的用户要求跳转到登录页面。我们通过给几个特定视图函数加装潢器完成了这个需求。然则今后增加的视图函数大概也须要加上装潢器,如许轻微有点烦琐,而运用中心件就很简朴了,固然这只是中心件的一个功用。

中心件

中心件的引见

什么是中心件?

官方的说法:中心件是一个用来处置惩罚Django的要乞降响应的框架级别的钩子。

它是一个轻量、初级别的插件体系,用于在全局范围内转变Django的输入和输出。每一个中心件组件都担任做一些特定的功用。

然则由于其影响的是全局,所以须要郑重运用,运用不当会影响机能。

说的直白一点中心件是协助我们在视图函数实行之前和实行以后都能够做一些分外的操纵,它本质上就是一个自定义类,类中定义了几个要领,Django框架会在要求的特定的时候去实行这些要领。

我们一向都在运用中心件,只是没有注重到罢了,翻开Django项目的Settings.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设置项是一个列表,列表中是一个个字符串,这些字符串现实上是一个个类,也就是一个个中心件。

我们相识的csrf就是一个中心件,在发送post要求是须要加上。

那接下来就说说中心件中的要领以及这些要领什么时候被实行。

自定义中心件

中心件能够定义五个要领,分别是:(重要的是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对象,则直接将该对象返回给用户。

自定义一个中心件示例

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_request

process_request有一个参数,就是request,这个request和视图函数中的request是一样的。

它的返回值能够是None也能够是HttpResponse对象。返回值是None的话,按一般流程继承走,交给下一个中心件处置惩罚,假如是HttpResponse对象,Django将不实行视图函数,而将响应对象返回给浏览器。

我们来看看多个中心件时,Django是怎样实行个中的process_request要领的。

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

在settings.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',
    'middlewares.MD1',  # 自定义中心件MD1
    'middlewares.MD2'  # 自定义中心件MD2
]

此时,接见一个视图,会发明终端中打印以下内容:

MD1里面的 process_request
MD2里面的 process_request
myapp 中的 index视图

把MD1和MD2的位置换取一下,再接见一个视图,会发明终端中打印的内容以下:

MD2里面的 process_request
MD1里面的 process_request
myapp 中的 index视图

看效果我们晓得:视图函数照样末了实行的,MD2比MD1先实行本身的process_request要领。

在打印一下两个自定义中心件中process_request要领中的request参数,会发明它们是同一个对象。

由此总结一下:

  1. 中心件的process_request要领是在实行视图函数之前实行的。
  2. 当设置多个中心件时,会根据MIDDLEWARE中的注册递次,也就是列表的索引值,夙昔到后顺次实行的。
  3. 差别中心件之间通报的request都是同一个对象

 

多个中心件中的process_response要领是根据MIDDLEWARE中的注册递次倒序实行的,也就是说第一个中心件的process_request要领起首实行,而它的process_response要领末了实行,末了一个中心件的process_request要领末了一个实行,它的process_response要领是最早实行。

process_response

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

给上述的M1和M2加上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


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

接见一个视图,看一下终端的输出:

MD2里面的 process_request
MD1里面的 process_request
myapp 中的 index视图
MD1里面的 process_response
MD2里面的 process_response

看效果可知:

process_response要领是在视图函数以后实行的,而且递次是MD1比MD2先实行。(此时settings.py中 MD2比MD1先注册)

多个中心件中的process_response要领是根据MIDDLEWARE中的注册递次倒序实行的,也就是说第一个中心件的process_request要领起首实行,而它的process_response要领末了实行,末了一个中心件的process_request要领末了一个实行,它的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要领并将应用到该HttpResponse并返回效果。

给MD1和MD2增加process_view要领:

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

接见index视图函数,看一下输出效果:

MD2里面的 process_request
MD1里面的 process_request
--------------------------------------------------------------------------------
MD2 中的process_view
<function index at 0x000001DE68317488> index
--------------------------------------------------------------------------------
MD1 中的process_view
<function index at 0x000001DE68317488> index
myapp 中的 index视图
MD1里面的 process_response
MD2里面的 process_response

process_view要领是在process_request以后,视图函数之前实行的,实行递次根据MIDDLEWARE中的注册递次夙昔到后递次实行的

process_exception

process_exception(self, request, exception)

该要领两个参数:

  一个HttpRequest对象

  一个exception是视图函数非常发生的Exception对象。

这个要领只要在视图函数中出现非常了才实行,它返回的值能够是一个None也能够是一个HttpResponse对象。假如是HttpResponse对象,Django将挪用模板和中心件中的process_response要领,并返回给浏览器,不然将默许处置惩罚非常。假如返回一个None,则交给下一个中心件的process_exception要领来处置惩罚非常。它的实行递次也是根据中心件注册递次的倒序实行。

 给MD1和MD2增加上这个要领:

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


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要领不实行。

想办法,在视图函数中抛出一个非常:

def index(request):
    print("myapp 中的 index视图")
    raise ValueError("呵呵")
    return HttpResponse("OK")

在MD1的process_exception中返回一个响应对象:

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))  # 返回一个响应对象

看输出效果:

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

注重,这里并没有实行MD2的process_exception要领,由于MD1中的process_exception要领直接返回了一个响应对象。

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

views.py中:

def index(request):
    print("myapp 中的 index视图")

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

接见index视图,终端输出的效果:

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

从效果看出:

视图函数实行完以后,马上实行了中心件的process_template_response要领,递次是倒序,先实行MD1的,在实行MD2的,接着实行了视图函数返回的HttpResponse对象的render要领,返回了一个新的HttpResponse对象,接着实行中心件的process_response要领。

中心件的实行流程

上一部分,我们相识了中心件中的5个要领,它们的参数、返回值以及什么时候实行,如今总结一下中心件的实行流程。

要求抵达中心件以后,先根据正序实行每一个注册中心件的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要领。

阿里云,服务器_Django 中间件

process_request要领都实行完后,婚配路由,找到要实行的视图函数,先不实行视图函数,先实行中心件中的process_view要领,process_view要领返回None,继承按递次实行,一切process_view要领实行完后实行视图函数。到场中心件3 的process_view要领返回了HttpResponse对象,则4,5,6的process_view以及视图函数都不实行,直接从末了一个中心件,也就是中心件6的process_response要领入手下手倒序实行。

阿里云,服务器_Django 中间件

process_template_response和process_exception两个要领的触发是有条件的,实行递次也是倒序。总结一切的实行流程以下:

阿里云,服务器_Django 中间件

阿里云,服务器_Django 中间件

中心件版登录考证 

中心件版的登录考证须要依托session,所以数据库中要有django_session表。

urls.py

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

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

views.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 == "links" 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.html

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

middleware.py

class LoginMiddleware(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))

在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',
    'middlewares.LoginMiddleware',
]

LoginMiddleware中心件注册后,一切的要求都要走LoginMiddleware的process_request要领。

接见的URL在白名单内或许session中有user用户名,则不做阻止走一般流程;

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

一般的URL然则须要登录后接见,让浏览器跳转到登录页面。

注:LoginMiddleware中心件中须要session,所以LoginMiddleware注册的位置要在session中心的下方。

 

附:Django要求流程图

阿里云,服务器_Django 中间件

腾讯云双十一活动