中間件

中間件的五個方法

中間件能夠定義五個方法,分別是:(主要的是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)

定義

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

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',
]
配置的是一個個路徑,封裝一些功能,至關於
from django.middleware.security import SecurityMiddleware

登陸驗證

條件:只有登陸成功才能訪問各個頁面,也就是不管點擊哪裏都跳轉到登陸界面html

用session時會報一個錯誤python

裝飾器版在cookie博客

中間件版登陸驗證

1.設置自定義中間件數據庫

在應用app01中django

在應用app01中建立一個包,隨便起名字,通常都放在一個叫作utils的包裏面,表示一個公用的組件,建立一個py文件,隨便起名字,例如叫作:middlewares.py,內容以下後端

from django.utils.deprecation import MiddlewareMixin

class LoginAuth(MiddlewareMixin):
    #請求來了,自動執行
    def process_request(self,request):
        print("請求來了")

而後去settings中的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',
    'app01.utils.mymiddleware.LoginAuth'
]

django的請求生命週期

具體解釋cookie

  1. 瀏覽器請求,wsgi.py,它封裝這socket,request對象,request對象給中間鍵,中間件根據middleware封裝的中間件一層一層向下執行,自動執行每一箇中間件中的請求函數session

  2. 執行順序從上到下,例如,咱們把自定義的中間件放在session中間件的上邊,那麼session就不起做用了,因此自定義的中間件是放在最下邊位置app

  3. 中間件加工完以後,給了url控制器,而後執行視圖函數views,視圖函數再經過orm與數據庫進行交互,而且渲染頁面socket

    注意若是此處沒有return值,默認返回的是None,表示正常,向下執行,若是return值是別的值,那麼就不會向下執行了,直接執行響應中間件的response方法給瀏覽器了

  4. 執行完成後,頁面須要間數據返還給瀏覽器,不須要通過url,可是還會通過中間件

  5. 中間件中執行process_response方法

因此由上引入白名單概念,就是讓不須要session的函數先執行,例如login函數

白名單

中間件的py文件中

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render,HttpResponse,redirect
class LoginAuth(MiddlewareMixin):

    white_list = ["/login/"]
    def process_request(self,request):
        print("請求來啦")
        path = request.path
        if path not in self.white_list:
            is_login = request.session.get("is_login")
            if not is_login:
                return redirect(f"/login/?requestUrl={path}")  # 路徑拼接,肯定跳轉前的路徑

    def process_response(self,request,response):
        print("請求走了")
        return response # 必須返回response對象

views.py 應用了中間件就不用了裝飾器了

from django.shortcuts import render,HttpResponse,redirect
def login(request):
    print("這是login函數")

    if request.method == "GET":
        return render(request, "login.html")
    else:
        username = request.POST.get("username")
        password = request.POST.get("password")
        if username == "alex" and password == "123":
            request.session["is_login"] = "yes"
            requestUrl = request.GET.get("requestUrl")
            return redirect(requestUrl if requestUrl else "home")
        else:
            return redirect("login")

def home(request):
    print("這是home函數")
    return render(request, "home.html",{"user":"woshishui"})

def index(request):
    print("這是index函數")
    return render(request, "index.html")

在上述代碼中,請求白名單中的數據時,根據請求路徑判斷下列條件的結果,當路徑在白名單中的時候,if條件不成立,默認返回None,正常執行views函數,若是訪問home函數等,覺得沒有在白名單中,執行if條件,若是已經有了session值,則默認返回None,正常執行函數,若是沒有session,則執行下一個if判斷,重定向到login路徑,再次請求,執行同上

中間件有多個方法時的執行順序

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.utils.mymiddleware.LoginAuth'
    'app01.utils.mymiddleware.MD1',  # 新增中間件
    "app01.utils.mymiddleware.MD2",  # 新增中加件
]

views.py

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.

def home(request):
    print("這是home函數")
    return render(request,"home.html")

def login(request):
    if request.method == "GET":
        print("這是login函數")
        return render(request,"login.html")
    else:
        user = request.POST.get("username")
        pwd = request.POST.get("password")
        if user == "alex" and pwd == "123":
            request.session["is_login"] = True
            return redirect("home")
        else:
            return redirect("login")

def index(request):
    print("這是index函數")
    return render(request,"index.html")

process_resquest 和 process_response 方法順序

middleware.py 中間件中文件

class MD1(MiddlewareMixin):
    def process_request(self,request):
        #request是一個請求的對象
        print("MD1的process_request方法",request)
        #return HttpResponse("ok")   ---------------------- 第一知識點  第一執行

    def process_response(self,request,response):
        print("MD1的process_response方法",response)
        return response
        #return HttpResponse("ok")  ---------------------- 第二知識點  第四執行

class MD2(MiddlewareMixin):
    def process_request(self,request):
        print("MD2的process_request方法",request)
        #return HttpResponse("ok")  ----------------------- 第三知識點   第二執行

    def process_response(self,request,response):
        print("MD2的process_response方法",response)
        #response 接收的是函數的返回值,是一個response對象
        return response
        #return HttpResponse("ok")  ---------------------- 第四知識點    第三執行

有return自定義內容的時候

都是get方法時驗證

第一知識點解釋

MD1的process_request方法 <WSGIRequest: GET '/index/'>
MD1的process_response方法 <HttpResponse status_code=200, "text/html; charset=utf-8">
當咱們在瀏覽器發送請求時,執行到中間件時就再也不向後執行,只執行返回函數process_response

第二知識點解釋

MD1的process_request方法 <WSGIRequest: GET '/index/'>
MD2的process_request方法 <WSGIRequest: GET '/index/'>
這是index函數
MD2的process_response方法 <HttpResponse status_code=200, "text/html; charset=utf-8">
MD1的process_response方法 <HttpResponse status_code=200, "text/html; charset=utf-8">
當咱們在瀏覽器發送請求時,因爲返回值的位置是最後執行的,因此全部的函數都執行了,可是因爲返回的是自定義的,而不是response對象,因此返回頁面不是html文件,而是ok

第三知識點解釋

MD1的process_request方法 <WSGIRequest: GET '/login/'>
MD2的process_request方法 <WSGIRequest: GET '/login/'>
MD2的process_response方法 <HttpResponse status_code=200, "text/html; charset=utf-8">
MD1的process_response方法 <HttpResponse status_code=200, "text/html; charset=utf-8">
當咱們在瀏覽器發送請求時,執行到中間件時就再也不向後執行,只執行返回函數process_response,依次向上執行(中間件順序) 因此返回頁面不是html文件,而是ok

第四知識點解釋

MD1的process_request方法 <WSGIRequest: GET '/login/'>
MD2的process_request方法 <WSGIRequest: GET '/login/'>
這是login函數
MD2的process_response方法 <HttpResponse status_code=200, "text/html; charset=utf-8">
MD1的process_response方法 <HttpResponse status_code=200, "text/html; charset=utf-8">
當咱們在瀏覽器發送請求時,因爲返回值的位置是最後執行的,因此全部的函數都執行了,可是因爲返回的是自定義的,而不是response對象,因此返回頁面不是html文件,而是ok

process_view

middleware.py

class MD1(MiddlewareMixin):
    def process_request(self,request):
        #request是一個請求的對象
        print("MD1的process_request方法",request)
        # return HttpResponse("ok")

    def process_response(self,request,response):
        print("MD1的process_response方法",response)
        return response
        # return HttpResponse("ok")

    def process_view(self,request,view_func,view_args,view_kwargs):
        print(view_func)  # 是個視圖函數,也就是執行此函數以前能夠獲得路由路徑
        print("MD1的process_view")

class MD2(MiddlewareMixin):
    def process_request(self,request):
        print("MD2的process_request方法",request)
        # return HttpResponse("ok")

    def process_response(self,request,response):
        print("MD2的process_response方法",response)
        #response 接收的是函數的返回值,是一個response對象
        return response
        # return HttpResponse("ok")

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

index請求結果

MD1的process_request方法 <WSGIRequest: GET '/login/'>
MD2的process_request方法 <WSGIRequest: GET '/login/'>
<function login at 0x0000002B48EDA2F0>
MD1的process_view
<function login at 0x0000002B48EDA2F0>
MD2的process_view
這是login函數
MD2的process_response方法 <HttpResponse status_code=200, "text/html; charset=utf-8">
MD1的process_response方法 <HttpResponse status_code=200, "text/html; charset=utf-8">

執行順序圖

process_exception

這個比較特殊,只有在有報錯的狀況下才執行,不然不執行

middleware.py

class MD1(MiddlewareMixin):
    def process_request(self,request):
        #request是一個請求的對象
        print("MD1的process_request方法",request)
        # return HttpResponse("ok")

    def process_response(self,request,response):
        print("MD1的process_response方法",response)
        return response
        # return HttpResponse("ok")

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

    def process_exception(self, request, exception):
        print(exception)
        print("MD1的process_exception")

class MD2(MiddlewareMixin):
    def process_request(self,request):
        print("MD2的process_request方法",request)
        # return HttpResponse("ok")

    def process_response(self,request,response):
        print("MD2的process_response方法",response)
        #response 接收的是函數的返回值,是一個response對象
        return response
        # return HttpResponse("ok")

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

    def process_exception(self, request, exception):
        print(exception)    #打印的是拋出的錯誤信息
        print("MD2的process_exception")

爲了報錯修改views.py代碼

def home(request):
    print("這是home函數")
    return render(request,"home.html")

def login(request):
    if request.method == "GET":
        print("這是login函數")
        raise ValueError("拋出錯誤")   #------注意此處要拋錯才能執行exception
        return render(request,"login.html")
    else:
        user = request.POST.get("username")
        pwd = request.POST.get("password")
        if user == "alex" and pwd == "123":
            request.session["is_login"] = True
            return redirect("home")
        else:
            return redirect("login")

def index(request):
    print("這是index函數")
    return render(request,"index.html")

執行結果

MD1的process_request方法 <WSGIRequest: GET '/login/'>
MD2的process_request方法 <WSGIRequest: GET '/login/'>
<function login at 0x0000000AD8F5A268>
MD1的process_view
<function login at 0x0000000AD8F5A268>
MD2的process_view
這是login函數
拋出錯誤
MD2的process_exception
拋出錯誤
MD1的process_exception
Internal Server Error: /login/
Traceback (most recent call last):
  File "C:\Python36\lib\site-packages\django\core\handlers\exception.py", line 41, in inner
    response = get_response(request)
  File "C:\Python36\lib\site-packages\django\core\handlers\base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "C:\Python36\lib\site-packages\django\core\handlers\base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "D:\Django項目\day67\zhongjianjian\app01\views.py", line 23, in login
    raise ValueError("拋出錯誤")
ValueError: 拋出錯誤
[16/Oct/2019 17:25:55] "GET /login/ HTTP/1.1" 500 65127
MD2的process_response方法 <HttpResponse status_code=500, "text/html">
MD1的process_response方法 <HttpResponse status_code=500, "text/html">

process_template_response

中間件文件寫法

from django.utils.deprecation import MiddlewareMixin
from django.middleware.csrf import MiddlewareMixin
class MD1(MiddlewareMixin):
    def process_template_response(self, request, response):
        print(response.template_name)  # 後端返回的html文件index.html
        print(response.context_data)   # 後端返回裝飾的鍵值對{"name":"dsb"}

        response.template_name = "home.html"
        response.context_data["user"] = "gunduzi" # 原來有就修改,沒有就增長
        print(response.template_name)  # 後端返回的html文件home.html
        print(response.context_data)  # 後端返回裝飾的鍵值對{'name': 'dsb', 'user': 'gunduzi'}

        print("MD2 process_template_response")
        return response

views.py

from django.shortcuts import render,HttpResponse,redirect
from django.template.response import TemplateResponse
def index(request):
    print("這是index函數")
    # return render(request, "index.html")
    return TemplateResponse(request, "index.html",{"name":"dsb"}) 

# 必須用TemplateResponse,不能用render

另外一種執行process_template_response的方法,瞭解,不推薦

def index(request):
    print("app01 中的 index視圖")
  #raise ValueError('出錯啦') 
    def render():
        print("in index/render")  
        #raise ValueError('出錯啦') #至於render函數中報錯了,那麼會先執行process_template_response方法,而後執行process_exception方法,若是是在render方法外面報錯了,那麼就不會執行這個process_template_response方法了。
        return HttpResponse("O98K") #返回的將是這個新的對象
    rep = HttpResponse("OK")
    rep.render = render
    return rep

中間件限制訪問頻率

相關字典知識

dic = {}
# 有就得到值,沒有爲None,
# 沒有時能夠給添加默認結果
print(dic.get("ip",[]))

dic = {"ip":[1,2,3]}
# 刪除具備返回值
print(dic["ip"].pop(0))

lst = []
for i in lst:
    print(i) # 什麼都沒有

題目:5秒時間內最多訪問3次

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

visit_history = {}

第一種方法

class LoginAuth(MiddlewareMixin):

    def process_request(self, request):
        # 獲取當前用戶的IP
        ip = request.META.get('REMOTE_ADDR')
        # print(ip)
        # 獲取當前的時間
        now = time.time()
        # 用戶沒有訪問過期將其訪問的時間點設爲列表中的鍵
        if ip not in visit_history:
            visit_history[ip] = [now]
            print(visit_history)
            return
        # 用戶第四次訪問時字典的鍵已經有3個值了,走這個條件
        elif len(visit_history[ip]) == 3:
            # 刪除掉第一個時間點,並將第四個時間點添加進去
            # pop具備返回值
            s1 = visit_history[ip].pop(0)
            visit_history[ip].append(now)
            print(visit_history)
            # 判斷從第一次到第四次時間是否超過5s
            if now - s1 <= 5:
                return HttpResponse('訪問過於頻繁')
        else:
            visit_history[ip].append(now)
            print(visit_history)

第二種方法

class LoginAuth(MiddlewareMixin):
# 第一次訪問,history爲空,第二次有1個值,第三次2個值,第四次3個值
# 只有第四次也在5s時間內時lst纔會保持3個值,不然就達不到3個,就能正常訪問

    def process_request(self, request):
        # 獲取當前用戶的IP
        ip = request.META.get('REMOTE_ADDR')
        # ip不在字典時結果爲[]
        history = visit_history.get(ip, [])
        # print(ip)
        # 獲取當前的時間
        now = time.time()
        # 用戶沒有訪問過期將其訪問的時間點設爲列表中的鍵
        # 每次訪問lst都是新的,是空列表
        lst = []
        # 第一次訪問,什麼都沒有,走最後兩句
        for i in history:
            if now - i < 5:
                # i是5秒內的時間 須要統計
                lst.append(i)
        print(lst)

        if len(lst) >= 3:
            # print(lst)
            return HttpResponse('頻率太快,先歇一會')

        lst.append(now)
        # print(lst)
        # 給字典添加鍵值對
        visit_history[ip] = lst

第三種方法

class LoginAuth(MiddlewareMixin):
# 第一次訪問,history爲空,第二次有1個值,第三次2個值,第四次3個值

    def process_request(self, request):
        # 獲取當前用戶的IP
        ip = request.META.get('REMOTE_ADDR')
        # ip不在字典時結果爲[]
        history = visit_history.get(ip, [])
        # print(ip)
        # 獲取當前的時間
        now = time.time()
        # history不爲空而且新值和最久的值時間差大於5秒,刪除舊值
        # 若是已有3個值,而且第4個值還在5秒內,那就不會執行while
        while history and now - history[-1] > 5:
            history.pop()

        if len(history) >= 3:
            return HttpResponse('頻率太快,先歇一會')
        # 注意新值插入的順序,要在最後
        history.insert(0,now)
        visit_history[ip] = history
相關文章
相關標籤/搜索