Django中間件

1、中間件的概念

  中間件顧名思義,是介於request與response處理之間的一道處理過程,相對比較輕量級,而且在全局上改變django的輸入與輸出。由於改變的是全局,因此須要謹慎實用,用很差會影響到性能。html

Django的中間件的定義:python

Middleware is a framework of hooks into Django’s request/response processing. <br>It’s a light, low-level 「plugin」 system for globally altering Django’s input or output.

  若是想修改請求,例如被傳送到view中的HttpRequest對象。 或者想修改view返回的HttpResponse對象,這些均可以經過中間件來實現。django

  可能還想在view執行以前作一些操做,這種狀況就能夠用 middleware來實現。瀏覽器

Django默認的Middleware能夠在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',
]

  MIDDLEWARE配置項是一個列表,列表中是一個個字符串,這些字符串實際上是一個個類,也就是一個個中間件。session

  咱們以前已經接觸過一個csrf相關的中間件了,以前將這一行註釋,再提交post請求的時候,就不會被forbidden了,後來學會使用csrf_token就不用再註釋這個中間件了。app

  手動引入一箇中間件:dom

from django.middleware.security import SecurityMiddleware

  查看中間件源碼:xss

import re

from django.conf import settings
from django.http import HttpResponsePermanentRedirect
from django.utils.deprecation import MiddlewareMixin


class SecurityMiddleware(MiddlewareMixin):
    def __init__(self, get_response=None):
        self.sts_seconds = settings.SECURE_HSTS_SECONDS
        self.sts_include_subdomains = settings.SECURE_HSTS_INCLUDE_SUBDOMAINS
        self.sts_preload = settings.SECURE_HSTS_PRELOAD
        self.content_type_nosniff = settings.SECURE_CONTENT_TYPE_NOSNIFF
        self.xss_filter = settings.SECURE_BROWSER_XSS_FILTER
        self.redirect = settings.SECURE_SSL_REDIRECT
        self.redirect_host = settings.SECURE_SSL_HOST
        self.redirect_exempt = [re.compile(r) for r in settings.SECURE_REDIRECT_EXEMPT]
        self.get_response = get_response

    def process_request(self, request):
        path = request.path.lstrip("/")
        if (self.redirect and not request.is_secure() and
                not any(pattern.search(path)
                        for pattern in self.redirect_exempt)):
            host = self.redirect_host or request.get_host()
            return HttpResponsePermanentRedirect(
                "https://%s%s" % (host, request.get_full_path())
            )

    def process_response(self, request, response):
        if (self.sts_seconds and request.is_secure() and
                'strict-transport-security' not in response):
            sts_header = "max-age=%s" % self.sts_seconds
            if self.sts_include_subdomains:
                sts_header = sts_header + "; includeSubDomains"
            if self.sts_preload:
                sts_header = sts_header + "; preload"
            response["strict-transport-security"] = sts_header

        if self.content_type_nosniff and 'x-content-type-options' not in response:
            response["x-content-type-options"] = "nosniff"

        if self.xss_filter and 'x-xss-protection' not in response:
            response["x-xss-protection"] = "1; mode=block"

        return response
SecurityMiddleware源碼

  每一箇中間件都有具體的功能。ide

2、自定義中間件

  中間件能夠定義五個方法,分別是:(主要的是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對象,則直接將該對象返回給用戶。

一、process_request, process_response

  當用戶發起請求的時候會依次通過全部的的中間件,這個時候的請求是process_request,最後到達views的函數中,views函數處理後,在依次穿過中間件,這個時候是process_response,最後返回給請求者。

  上述截圖中的中間件都是django中的,咱們也能夠本身定義一箇中間件,本身寫一個類,可是必須繼承MiddlewareMixin。

  自定義中間件須要引入:

from django.utils.deprecation import MiddlewareMixin

  

在視圖函數中:

def index(request):
    print("index.....")
    return HttpResponse("Index")

在自定義中間件my_middlewares.py中:

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

class CustomerMiddleware(MiddlewareMixin):

    def process_request(self, request):
        print("CustomMiddleware process_request...")

    def process_response(self, request, response):
        # 必須有返回值,要一層層往回傳,不加就會報錯
        print("CustomMiddleware process_response")
        return response

class CustomerMiddleware2(MiddlewareMixin):

    def process_request(self, request):
        print("CustomMiddleware2 process_request...")

    def process_response(self, request, response):
        print("CustomMiddleware2 process_response")
        return response

 訪問index頁面,控制檯輸出結果:

CustomMiddleware process_request...
CustomMiddleware2 process_request...
index.....
CustomMiddleware2 process_response
CustomMiddleware process_response

  注意:1 .在process_request函數中,若是添加了return語句,會發生中斷現象,程序把請求發給中間件,而後依次返回給請求者。

     2.在process_response函數中,必定要有return語句,由於須要一層層往回傳值給請求者(瀏覽器)。

(1)若是是在CustomerMiddleware類中的process_request函數中添加return  HttpResponse("forbidden..."),輸出以下:

網頁顯示:
forbidden...

控制檯輸出:
CustomMiddleware process_request...
CustomMiddleware process_response

(2)若是是在CustomerMiddleware2類中的process_request函數中添加return  HttpResponse("forbidden..."),輸出以下:

網頁顯示:
forbidden...

控制檯輸出
CustomMiddleware process_request...
CustomMiddleware2 process_request...
CustomMiddleware2 process_response
CustomMiddleware process_response

 流程圖以下:

  

二、process_view

def process_view(self, request, callback, callback_args, callback_kwargs):...

  my_middlewares.py修改以下:

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

class CustomerMiddleware(MiddlewareMixin):

    def process_request(self, request):
        print("CustomMiddleware process_request...")

    def process_response(self, request, response):
        # 必須有返回值,要一層層往回傳,不加就會報錯
        print("CustomMiddleware process_response")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("CustomMiddleware1  process_view")

class CustomerMiddleware2(MiddlewareMixin):

    def process_request(self, request):
        print("CustomMiddleware2 process_request...")

    def process_response(self, request, response):
        print("CustomMiddleware2 process_response")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("CustomMiddleware2  process_view")

  執行結果以下所示:

網頁顯示:
index

控制檯輸出
CustomMiddleware process_request...
CustomMiddleware2 process_request...
CustomMiddleware1  process_view
CustomMiddleware2  process_view
index.....
CustomMiddleware2 process_response
CustomMiddleware process_response

  上述代碼運行過程見下圖:

  

  當最後一箇中間件的process_request到達路由關係映射以後,返回到中間件1的process_view,而後依次往下,到達views函數,最後經過process_response依次返回到達用戶。

  意義:在中間件走完時,多走了一步路由控制,而callback參數就是用來找到此次請求對應的視圖函數(callback_args是視圖函數的參數),所以能夠提早一步來執行視圖函數,而若是return直接返回,則能夠跳過視圖函數這一步直接返回。

示例1:用process_view來調用視圖函數

  僅修改CustomMiddleware2的process_view函數,修改以下:

class CustomerMiddleware2(MiddlewareMixin):

    def process_request(self, request):
        print("CustomMiddleware2 process_request...")
        # return HttpResponse("forbidden...")

    def process_response(self, request, response):
        print("CustomMiddleware2 process_response")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("====》", callback(callback_args))
        print("CustomMiddleware2  process_view")

  輸出以下所示:

CustomMiddleware process_request...
CustomMiddleware2 process_request...
CustomMiddleware1  process_view
index.....
====》 <HttpResponse status_code=200, "text/html; charset=utf-8">
CustomMiddleware2  process_view
index.....
CustomMiddleware2 process_response
CustomMiddleware process_response

 示例2:給process_view函數返回值

  僅修改CustomMiddleware2的process_view函數,修改以下:

class CustomerMiddleware2(MiddlewareMixin):

    def process_request(self, request):
        print("CustomMiddleware2 process_request...")
        # return HttpResponse("forbidden...")

    def process_response(self, request, response):
        print("CustomMiddleware2 process_response")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        # print("====》", callback(callback_args))
        print("CustomMiddleware2  process_view")
        return HttpResponse("123")

輸出以下所示:

頁面顯示:
123

控制檯輸出:
CustomMiddleware process_request...
CustomMiddleware2 process_request...
CustomMiddleware1  process_view
CustomMiddleware2  process_view
CustomMiddleware2 process_response
CustomMiddleware process_response

  注意:process_view若是有返回值,會越過其餘的process_view以及視圖函數,可是全部的process_response都還會執行。

三、process_exception 

def process_exception(self, request, exception):...

  示例修改以下:

class CustomerMiddleware(MiddlewareMixin):

    def process_request(self, request):
        print("CustomMiddleware1 process_request...")

    def process_response(self, request, response):
        # 必須有返回值,要一層層往回傳,不加就會報錯
        print("CustomMiddleware1 process_response")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("CustomMiddleware1  process_view")

    def process_exception(self, request, exception):
        print("CustomMiddleware1 process_exception")


class CustomerMiddleware2(MiddlewareMixin):

    def process_request(self, request):
        print("CustomMiddleware2 process_request...")

    def process_response(self, request, response):
        print("CustomMiddleware2 process_response")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("CustomMiddleware2  process_view")

    def process_exception(self, request, exception):
        print("CustomMiddleware2 process_exception")

  輸出以下所示:

網頁顯示:
Index

控制檯輸出:
CustomMiddleware1 process_request...
CustomMiddleware2 process_request...
CustomMiddleware1  process_view
CustomMiddleware2  process_view
index.....
CustomMiddleware2 process_response
CustomMiddleware1 process_response

  注意:在代碼正常狀況下不會執行,當視圖中報錯時,纔會依次執行process_excepsion和process_response。

  當views出現錯誤時流程圖以下所示:

  

(1)在視圖函數中填寫錯誤代碼

def index(request):
    print("index.....")
    yuan
    return HttpResponse("Index")

  輸出以下:

  1)頁面顯示錯誤提示

  2)控制檯輸出

CustomMiddleware1 process_request...
CustomMiddleware2 process_request...
CustomMiddleware1  process_view
CustomMiddleware2  process_view
Internal Server Error: /index/
index.....
CustomMiddleware2 process_exception
CustomMiddleware1 process_exception
大段的錯誤提示
CustomMiddleware2 process_response
CustomMiddleware1 process_response

(2)在process_exception中抓取異常信息,返回到頁面中顯示

  僅對CustomerMiddleware2中的process_exception修改:

class CustomerMiddleware2(MiddlewareMixin):

    def process_request(self, request):
        print("CustomMiddleware2 process_request...")
        # return HttpResponse("forbidden...")

    def process_response(self, request, response):
        print("CustomMiddleware2 process_response")
        return response

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("CustomMiddleware2  process_view")

    def process_exception(self, request, exception):
        print("CustomMiddleware2 process_exception")
        return HttpResponse(exception)

  輸出以下:

頁面顯示:
name 'yuan' is not defined

控制檯輸出:
CustomMiddleware1 process_request...
CustomMiddleware2 process_request...
CustomMiddleware1  process_view
CustomMiddleware2  process_view
index.....
CustomMiddleware2 process_exception
CustomMiddleware2 process_response
CustomMiddleware1 process_response

  CustomMiddleware2的process_exception在捕獲到錯誤後,把return值做爲響應體直接返回了,就再也不執行後面的exception了(CustomMiddleware1的),再依次傳給process_response即返回給瀏覽器了。

3、中間件的應用

一、作IP訪問頻率限制

  某些IP訪問服務器的頻率太高,進行攔截,好比限制每分鐘不能超過20次。

 

 

 

二、URL訪問過濾

  若是用戶訪問的是login視圖(放過)

  若是訪問其餘視圖,須要檢測是否是有session認證,已經有了放行,沒有返回login,這樣就免得在多個視圖函數上寫裝飾器了!

(1)在settings.py中定義白名單:

# 白名單
WHITE_LIST = ["/login/", "/reg/", "/logout/"]

(2)建立自定義的中間件文件./app01/my_middlewares.py

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse, redirect
from authDemo import settings

class AutoMiddleware(MiddlewareMixin):

    def process_request(self, request):
        # 拿到白名單
        white_list = settings.WHITE_LIST
        if request.path in white_list:
            # 路徑在白名單中直接經過
            return None    # return None等同於不寫return ,中間件經過

        # 不在白名單中的路徑須要校驗是否登陸驗證
        if not request.user.is_authenticated:
            # 未通過校驗跳轉到登陸頁面
            return redirect("/login/")

(3)在settings中添加自定義中間件

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.my_middlewares.AutoMiddleware",
]

  運行程序,並訪問index頁面,發現跳轉到登陸頁面。

  雖然中間件很方便,但不是全部狀況下都應該用中間件,稍有不慎就會下降程序效率。每每視圖函數大多數都須要校驗,則使用中間件比較合適,只有少許須要校驗則仍是使用@login_required裝飾器更加合適。

4、中間件源碼試讀

  主要嘗試着讀如下兩個自帶的中間件:

'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
相關文章
相關標籤/搜索