Django - 中間件html
一. 什麼是中間件數據庫
官方: 中間件是一個用來處理Django的請求和相應的框架級別的鉤子, 他是一個輕量, 低級別的插件系統, 用於在全局範圍內改變Django的輸入和輸出, 每一箇中間件都負責作一些特定的功能.django
大白話: 中間件是幫助咱們在視圖函數執行以前和執行以後均可以作一些額外的操做, 本質就是一個自定類, 類中定義了幾個方法,瀏覽器
Django框架會處理請求的特定的時間去執行這些方法. 影響的是全局, 謹慎使用.cookie
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', ]
二. 定義中間件session
中間件定義能夠定義五個方法, (主要的是 process_request和 process_response)app
process_request(self.rerquest)框架
process_view(self, request, view_func, view_args, view_kwargs)ide
process_template_response(self, request, response)函數
process_exception(self, request, exception)
process_response(self, request, respoonse)
以上方法的返回值能夠是None或HttpResponse對象, 若是是None, 則繼續按照django定義的規則向後繼續進行, 若是是HttpResponse對象, 則直接將該對象返回給用戶.
1. process_request(self.rerquest)
執行時間: 在視圖函數執行以前
參數: rsquest --> 跟視圖函數中的是同一個
執行順序: 按照註冊順序, 順序執行
返回值: None --> 正常路程
HttpResponse對象 --> 不執行後面中間中的procrss_request方法, 不執行視圖函數, 直接執行當前中間件中的process_response 方法, 後面正常走.
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")
在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
總結:
2. process_response(self, request, respoonse)
執行時間: 在視圖函數執行以後, process_request以後, 路由匹配以後
參數: request --> 跟函數中的是同一個
response --> 視圖函數中傳遞的相應對象
執行順序: 按照註冊順序, 倒敘執行
返回值: None -->正常流程
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 class MD2(MiddlewareMixin): def process_request(self, request): print("MD2裏面的 process_request") def process_response(self, request, response): print("MD2裏面的 process_response") return response
總結:
process_response方法是在視圖函數以後執行的,而且順序是MD1比MD2先執行。(此時settings.py中 MD2比MD1先註冊)
多箇中間件中的process_response方法是按照MIDDLEWARE中的註冊順序倒序執行的,也就是說第一個中間件的process_request方法首先執行,而它的process_response方法最後執行,最後一箇中間件的process_request方法最後一個執行,它的process_response方法是最早執行。
3.process_view(self, request, view_func, view_args, view_kwargs)
執行時間: 在process_request方法以後, 在視圖函數執行以前
參數: request --> 跟視圖函數的是同一個
view_func --> 視圖函數
view_args --> 視圖函數的位置函數
view_kwargs --> 視圖函數的關鍵字參數
執行順序: 按照註冊順序 順序執行
返回值: None --> 正常執行
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 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") 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__)
4. process_exception(self, request, exception)
執行時間:出現異常的時候才執行
參數:request ——》 跟視圖函數中的是同一個
exception——》 異常對象
執行順序:按照註冊順序 倒敘執行
返回值:None: 正常流程
HttpResponse對象:不執行後面中間中的process_exception方法,直接執行最後一箇中間件中的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 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") 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")
5. process_template_response(self, request, response)
執行時間:視圖函數返回的response對象有一個叫render的方法
參數:request ——》 跟視圖函數中的是同一個
response ——》 視圖函數中傳遞的響應對象
執行順序:按照註冊順序 倒敘執行
返回值:必須返回response對象
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") 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
三. 中間件的執行流程
四. 中間件版登陸註冊
注意: 要把session 同步到數據庫中
urls.py
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, name='login'), url(r'^index/$', views.index, name='index'), url(r'^home/$', views.home, name='home'), ]
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 == "alex" and pwd == "alex3714": # 設置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' %}" method="post"> {% csrf_token %} <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>
middlewares.py
rom django.utils.deprecation import MiddlewareMixin class AuthMD(MiddlewareMixin): white_list = ['/login/', ] # 白名單 black_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.black_list: return HttpResponse('This is an illegal URL') # 白名單的網址或者登錄用戶不作限制 elif next_url in self.white_list or request.session.get("user"): return 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', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'middlewares.AuthMD' ]
AuthMD中間件註冊後,全部的請求都要走AuthMD的process_request方法。
若是URL在黑名單中,則返回This is an illegal URL的字符串;
訪問的URL在白名單內或者session中有user用戶名,則不作阻攔走正常流程;
正常的URL可是須要登陸後訪問,讓瀏覽器跳轉到登陸頁面。
注:AuthMD中間件中須要session,因此AuthMD註冊的位置要在session中間的下方。
# visit_dict = {} # class Throttle(MiddlewareMixin): # # def process_request(self, request): # # 獲取到ip # ip = request.META.get('REMOTE_ADDR') # # 獲取到訪問記錄 # history = visit_dict.get(ip, []) # if not history: # visit_dict[ip] = history # # now = time.time() # new = [] # for i in history: # if now - i > 5: # new.append(i) # for i in new: # history.remove(i) # # while history and now - history[-1] > 5: # history.pop() # # if len(history) >= 3: # return HttpResponse('訪問頻率太快了') # # history.insert(0, now)
class Throttle(MiddlewareMixin): def process_request(self, request): history = request.session.get('history', []) now = time.time() while history and now - history[-1] > 10: history.pop() if len(history) >= 3: return HttpResponse('你的訪問頻率太快') history.insert(0, now) request.session['history'] = history
五. 補充
1. 跨站請求僞造, csrf中間件
兩個裝飾器
from django.views.decorator.csrf import csrf_exempt, csrf_protect
@carf_exempt --> 給視圖加上裝飾器後, 當前的視圖不須要CSRF校驗
@csrf_protect --> 給視圖加上裝飾器後, 當前的視圖須要CSRF校驗
2. process_request
從cookie中獲取csrftoken的值 —— 》 request.META['CSRF_COOKIE']
3. process_view
1. 視圖函數加上csrf_exempt裝飾器,不進行CSRF校驗 2. 請求方式 是'GET', 'HEAD', 'OPTIONS', 'TRACE' 也不進行校驗 3. csrf_token = request.META.get('CSRF_COOKIE') # cookie中獲取csrftoken的值 # 獲取提交的csrfmiddlewaretoken的值 request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') 若是或許不到csrfmiddlewaretoken的值 再嘗試從請求頭中獲取X_CSRFTOKEN的值 —— 》request_csrf_token 4. request_csrf_token 和 csrf_token 進行比較 1. 能比較成功 經過校驗 2. 不能比較成功 拒絕