1. 程式人生 > >python中介軟體 之進階

python中介軟體 之進階

 

中介軟體簡介

    中介軟體是一個用來處理django的響應與請求的框架級別的鉤子.它是一個輕量,低級別的外掛系統,
    用於在全域性範圍內改變django的輸入和輸出,每個中介軟體元件都負責做一些特定的功能.

 

    ----簡單來說,Django的中介軟體是一個類。用來在全域性範圍內處理請求和響應,會包含以下幾個方法:

 

    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)

    1. process_request
        1. 執行時間
            在檢視函式之前執行
        2. 引數
            request 和檢視中的request是同一個
        3. 返回值
            返回None 
            返回response物件  
                不執行後面中間的process_request方法和檢視
                直接執行當前值中介軟體的process_response方法
        4. 執行順序
            按照註冊的順序執行
           

    2. process_response   
        1. 執行時間   
            在檢視函式之後執行
        2. request, response
            request 和檢視中的request是同一個
            response 返回的response物件
        3. 返回值
            返回response物件
        4. 執行順序
            按照註冊的倒序執行
   

    3. process_view
        1. 執行時間
            在檢視函式之前,process_request之後執行
        2. 引數
            view_func  將要執行的檢視函式
            view_args  檢視函式的可變長位置引數
            view_kwargs    檢視函式的可變長關鍵字引數
        3. 返回值
            返回  None  正常執行
            返回  response物件   不執行後面的process_view和檢視,直接執行所有中介軟體的process_response方法

        4。執行順序
            按照註冊的順序執行

    4. process_exception(有條件觸發:有錯誤才執行)
                1. 執行時間
                    在檢視函式之後,process_response之前執行
                2. 引數
                    exception  錯誤物件
                3. 返回值
                    返回  None  不對錯誤進行處理,交給下一個中介軟體進行處理
                    返回  response物件  下一個中間的process_exception不執行,直接執行所有中介軟體的process_response方法
                4. 執行順序
                    按照註冊的倒序執行
    5. process_template_response(條件觸發:檢視返回的response有render方法)
        1. 執行時間
            在檢視函式之後,process_response之前執行
        2. 引數
        3. 返回值
            返回 response物件
        4. 執行順序
            按照註冊的倒序執行,執行完所有的process_template_response方法後執行response.render方法
   

複製程式碼

 

 

                                           中介軟體的執行流程                           

上一部分,我們瞭解了中介軟體中的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方法。

 

process_request方法都執行完後,匹配路由,找到要執行的檢視函式,先不執行檢視函式,先執行中介軟體中的process_view方法,process_view方法返回None,繼續按順序執行,所有process_view方法執行完後執行檢視函式。假如中介軟體3 的process_view方法返回了HttpResponse物件,則4,5,6的process_view以及檢視函式都不執行,直接從最後一箇中間件,也就是中介軟體6的process_response方法開始倒序執行。

process_template_response和process_exception兩個方法的觸發是有條件的,執行順序也是倒序。總結所有的執行流程如下:

 

 

 小練習:

(1)

AuthMD中介軟體註冊後,所有的請求都要走AuthMD的process_request方法。

如果URL在黑名單中,則返回This is an illegal URL的字串;

訪問的URL在白名單內或者session中有user使用者名稱,則不做阻攔走正常流程;

正常的URL但是需要登入後訪問,讓瀏覽器跳轉到登入頁面。

注:AuthMD中介軟體中需要session,所以AuthMD註冊的位置要在session中間的下方。 

 

複製程式碼

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

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/',views.login),
    url(r'^home/',views.home),
    url(r'^index/',views.index),
    url(r'^logout/',views.logout),
]

複製程式碼

urls

from django.shortcuts import render,HttpResponse,redirect
from django.conf import global_settings
# Create your views here.

def login(request):
    err_msg = ""
    if request.method == "POST":
        name = request.POST.get("name")
        pwd = request.POST.get("pwd")
        if name == "alex" and pwd == "123":
            request.session['user'] = "user"
            # request.session.set_expiry(0)   #設定關閉瀏覽器後自動登出
            return_path = request.GET.get("return","")
            if return_path:
                return redirect(return_path)
            else:
                return redirect("/home/")

        else:
            err_msg = "使用者名稱或者密碼錯誤"
    return render(request,"login.html",{"err_msg":err_msg})


# 登陸裝飾器
# def wrap(func):
#     def inner(request,*args,**kwargs):
#         if request.session.get("is_login","") == "True":
#             ret = func(request,*args,**kwargs)
#             return ret
#         else:
#             path = request.path_info
#             return redirect("/login/?return={}".format(path))
#     return inner


# @wrap
def home(request):
    return render(request,"home.html")


# @wrap
def index(request):
    return render(request,"index.html")



def logout(request):
    del request.session['user']
    return redirect("/login/")

Views檔案

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


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

    def process_request(self, request):
        path = request.path_info
        if path in self.black_list:
            return HttpResponse("這是黑名單的網址")
        elif path in self.white_list or request.session.get("user"):
            print(11)
            return
        else:
            print(22)
            return redirect("/login/?return={}".format(path))

中介軟體

'app01.my_middlewaremixin.Login_middle'

settings註冊中介軟體

#login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post">
    {% csrf_token %}
    <p>使用者名稱:<input type="text" name="name"></p>
    <p>密碼:<input type="password" name="pwd"></p>
    <p><button>提交</button></p><span>{{ err_msg }}</span>
</form>
</body>
</html>


#index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>歡迎登陸index頁面</h1>
</body>
</html>



#home.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>歡迎登陸home頁面</h1>
<a href="/logout/">登出</a>
</body>
</html>

template檔案 (三個)

 

(2) 限制訪問頻率,一分鐘只能訪問3次.
    思路:根據IP做判斷,1分鐘只能訪問3次
          1. 獲取ip
          2. 記錄時間
          3. 根據訪問記錄做判斷
 

import time

l = {}
class Throttle(MiddlewareMixin):
    def process_request(self, request):

        ip = request.META.get("REMOTE_ADDR")
        now = time.time()
        if not l.get(ip, ""):
            l[ip] = []
        history = l[ip]
        x = []
        for i in history:
            if now - i < 5:
                x.append(i)
                print(x)
        history = x
        print(history)
        if len(history) >= 3:
            return HttpResponse("次數太多了")
        history.insert(0, now)
        l[ip] = history

中介軟體程式碼

request.META 可以獲取到所有內容,可以從中找你想用的東西.一組組鍵值對兒

複製程式碼

# {'ALLUSERSPROFILE': 'C:\\ProgramData',
#  'APPDATA': 'C:\\Users\\zhanghutao\\AppData\\Roaming',
#  'COMMONPROGRAMFILES': 'C:\\Program Files\\Common Files',
#  'COMMONPROGRAMFILES(X86)': 'C:\\Program Files (x86)\\Common Files',
#  'COMMONPROGRAMW6432': 'C:\\Program Files\\Common Files',
#  'COMPUTERNAME': 'DESKTOP-30PDHUE',
#  'COMSPEC': 'C:\\WINDOWS\\system32\\cmd.exe',
#  'DJANGO_SETTINGS_MODULE': '中介軟體.settings',
#  'HOMEDRIVE': 'C:', 'HOMEPATH': '\\Users\\zhanghutao',
#  'LOCALAPPDATA': 'C:\\Users\\zhanghutao\\AppData\\Local',
#  'LOGONSERVER': '\\\\DESKTOP-30PDHUE',
#  'MOZ_PLUGIN_PATH': 'E:\\Foxit Reader\\plugins\\',
#  'NUMBER_OF_PROCESSORS': '4',
#  'ONEDRIVE': 'C:\\Users\\zhanghutao\\OneDrive',
#  'OS': 'Windows_NT',
#  'PATH': 'C:\\WINDOWS\\system32;'
# 'C:\\WINDOWS;'
# 'C:\\WINDOWS\\System32\\Wbem;'
#  'C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;'
#          'C:\\Program Files\\WIDCOMM\\Bluetooth Software\\;C:\\Program Files\\WIDCOMM\\Bluetooth Software\\syswow64;D:\\mysql\\mysql-5.7.23-winx64\\bin;;D:\\PPB\\PBB Reader\\x64;D:\\python3.6\\Scripts\\;D:\\python3.6\\;C:\\Users\\zhanghutao\\AppData\\Local\\Microsoft\\WindowsApps;;D:\\python3.6\\lib\\site-packages\\pywin32_system32;D:\\python3.6\\lib\\site-packages\\pywin32_system32', 'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC', 'PROCESSOR_ARCHITECTURE': 'AMD64', 'PROCESSOR_IDENTIFIER': 'Intel64 Family 6 Model 58 Stepping 9, GenuineIntel', 'PROCESSOR_LEVEL': '6', 'PROCESSOR_REVISION': '3a09', 'PROGRAMDATA': 'C:\\ProgramData', 'PROGRAMFILES': 'C:\\Program Files', 'PROGRAMFILES(X86)': 'C:\\Program Files (x86)', 'PROGRAMW6432': 'C:\\Program Files', 'PSMODULEPATH': 'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\\Modules;C:\\Program Files\\Intel\\Wired Networking\\', 'PUBLIC': 'C:\\Users\\Public', 'PYCHARM_HOSTED': '1', 'PYCHARM_MATPLOTLIB_PORT': '54693', 'PYTHONIOENCODING': 'UTF-8', 'PYTHONPATH': 'D:\\pycharm\\PyCharm 2018.1.3\\helpers\\pycharm_matplotlib_backend;D:\\PythonTest\\django專案\\中介軟體', 'PYTHONUNBUFFERED': '1', 'SESSIONNAME': 'Console', 'SYSTEMDRIVE': 'C:', 'SYSTEMROOT': 'C:\\WINDOWS', 'TEMP': 'C:\\Users\\ZHANGH~1\\AppData\\Local\\Temp', 'TMP': 'C:\\Users\\ZHANGH~1\\AppData\\Local\\Temp', 'USERDOMAIN': 'DESKTOP-30PDHUE', 'USERDOMAIN_ROAMINGPROFILE': 'DESKTOP-30PDHUE', 'USERNAME': 'zhanghutao', 'USERPROFILE': 'C:\\Users\\zhanghutao', 'WINDIR': 'C:\\WINDOWS', 'RUN_MAIN': 'true', 'SERVER_NAME': 'DESKTOP-30PDHUE', 'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_PORT': '8000', 'REMOTE_HOST': '', 'CONTENT_LENGTH': '', 'SCRIPT_NAME': '', 'SERVER_PROTOCOL': 'HTTP/1.1', 'SERVER_SOFTWARE': 'WSGIServer/0.2', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/index/', 'QUERY_STRING': '',
#  'REMOTE_ADDR': '127.0.0.1',
#  'CONTENT_TYPE': 'text/plain',
#  'HTTP_HOST': '127.0.0.1:8000',
#  'HTTP_CONNECTION': 'keep-alive', 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.170 Safari/537.36', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9', 'HTTP_COOKIE': 'csrftoken=HSERiDbpYwWLfLhrjDIgcsMP8HjD1CqXuSPjgSD40l8YpqbMPTYaNKl3xm6MLWne; sessionid=qfrad5a76h2ozt0erj74wef1nrzsl63n', 'wsgi.input': <_io.BufferedReader name=964>, 'wsgi.errors': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, 'wsgi.version': (1, 0), 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.multithread': True, 'wsgi.multiprocess': False, 'wsgi.file_wrapper': <class 'wsgiref.util.FileWrapper'>, 'CSRF_COOKIE': 'HSERiDbpYwWLfLhrjDIgcsMP8HjD1CqXuSPjgSD40l8YpqbMPTYaNKl3xm6MLWne'}

複製程式碼