中間件能夠定義五個方法,分別是:(主要的是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的輸入與輸出。由於改變的是全局,因此須要謹慎實用,用很差會影響到性能。
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
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' ]
具體解釋cookie
瀏覽器請求,wsgi.py,它封裝這socket,request對象,request對象給中間鍵,中間件根據middleware封裝的中間件一層一層向下執行,自動執行每一箇中間件中的請求函數session
執行順序從上到下,例如,咱們把自定義的中間件放在session中間件的上邊,那麼session就不起做用了,因此自定義的中間件是放在最下邊位置app
中間件加工完以後,給了url控制器,而後執行視圖函數views,視圖函數再經過orm與數據庫進行交互,而且渲染頁面socket
注意若是此處沒有return值,默認返回的是None,表示正常,向下執行,若是return值是別的值,那麼就不會向下執行了,直接執行響應中間件的response方法給瀏覽器了
執行完成後,頁面須要間數據返還給瀏覽器,不須要通過url,可是還會通過中間件
中間件中執行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") ---------------------- 第四知識點 第三執行
都是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
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">
執行順序圖
這個比較特殊,只有在有報錯的狀況下才執行,不然不執行
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">
中間件文件寫法
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) # 什麼都沒有
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