在研究中間件的時候咱們首先要知道html
1 什麼是中間件?數據庫
官方的說法:中間件是一個用來處理Django的請求和響應的框架級別的鉤子。它是一個輕量、低級別的插件系統,用於在全局範圍內改變Django的輸入和輸出。每一箇中間件組件都負責作一些特定的功能。django
可是因爲其影響的是全局,因此須要謹慎使用,使用不當會影響性能。瀏覽器
說的直白一點中間件是幫助咱們在視圖函數執行以前和執行以後均可以作一些額外的操做,它本質上就是一個自定義類,類中定義了幾個方法,Django框架會在請求的特定的時間去執行這些方法。安全
2 中間件的做用?服務器
中間件主要就是用於在全局範圍內改變Django的輸入和輸出。一個請求過來在進行處理以前要先通過中間件,同時相應的時候也要通過中間件。session
3 Django中的中間件咱們能夠經過查看settings中的MIDDLEWARE:app
那麼看到這個咱們知道咱們最開始的時候在不註釋掉 ====》'django.middleware.csrf.CsrfViewMiddleware', 這一句的時候,咱們的請求有時候會有一個大黃頁:forbidden。。。等這些內容,那麼這個時候咱們其實就能夠解釋爲何了:框架
csrf_token,當咱們使用瀏覽器訪問一個服務器的時候若是沒有攜帶csrf_token那麼至關於這個網站是不安全的,那麼服務器是不會對你的請求進行相應的,也就返回給你一個forbidden。
那麼下面咱們經過一個圖來看下中間件在Django中的位置:ide
自定義中間件:
中間件能夠定義五個方法,分別是:(主要的是process_request和process_response)
process_request(self,request) #在urls以前處理請求。
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(self,request)
process_response(self, request, response)
那麼從這個兩個函數的的參數,咱們能夠知道,通常狀況下request是不須要返回值的,由於若是他有返回值的話,咱們的請求直接就在中間件就給他返回了,而沒有通過咱們的一個視圖函數。
而response是須要返回值的,由於他是按照列表倒序執行的,那麼下一步的操做須要上一步的返回值才能繼續進行操做,否則會報錯。
中間件五大方法:
1 中間件process_request(self,request)
process_request有一個參數,就是request,這個request和視圖函數中的request是同樣的。
它的返回值能夠是None也能夠是HttpResponse對象。返回值是None的話,按正常流程繼續走,交給下一個中間件處理,若是是HttpResponse對象,Django將不執行視圖函數,而將相應對象返回給瀏覽器。
我麼能夠新建立一個py文件,也能夠在一個package裏面建立,只要咱們在settings裏的MIDDLEWARE中添加進去就能夠。
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class Middleware1(MiddlewareMixin): def process_request(self, request): print('這是中間件middle1!!!') # return HttpResponse('中間件request的返回值') class Middleware2(MiddlewareMixin): def process_request(self, request): print('這是中間件middle12!!')
那麼這個時候咱們再經過瀏覽器訪問的時候,pycharm的終端中就會打印相應的消息,咱們經過這個能夠的出結論:
1 中間件的process_request方法是在執行視圖函數以前執行的。
2 當配置多箇中間件時,會按照MIDDLEWARE中的註冊順序,也就是列表的索引值,從前到後依次執行的。
3 不一樣中間件之間傳遞的request都是同一個對象
2 中間件process_response(self, request, response)
它有兩個參數,一個是request,一個是response,request就是上述例子中同樣的對象,response是視圖函數返回的HttpResponse對象。該方法的返回值也必須是HttpResponse對象。
那麼咱們在1 process_request(self,request)的基礎上再添加類與方法:
from django.utils.deprecation import MiddlewareMixin
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse class Middleware1(MiddlewareMixin): def process_request(self, request): print('這是中間件middle1!!!') # return HttpResponse('中間件request的返回值')
def process_response(self,request,response)
print('這是M1的響應')
return response class Middleware2(MiddlewareMixin): def process_request(self, request): print('這是中間件middle12!!')
def process_response(self,request,response)
print('這是M1的響應')
return response
那麼一樣的咱們從pycharm終端打印的結果得出結論:
process_response方法是在視圖函數以後執行的,而且順序是MD1比MD2先執行。(此時settings.py中 MD2比MD1先註冊)
多箇中間件中的process_response方法是按照MIDDLEWARE中的註冊順序倒序執行的,也就是說第一個中間件的process_request方法首先執行,而它的process_response方法最後執行,
最後一箇中間件的process_request方法最後一個執行,它的process_response方法是最早執行。
3 中間件process_view:
process_view(self, request, view_func, view_args, view_kwargs)
該方法有四個參數
request是HttpRequest對象。
view_func是Django即將使用的視圖函數。 (它是實際的函數對象,而不是函數的名稱做爲字符串。)
view_args是將傳遞給視圖的位置參數的列表.
view_kwargs是將傳遞給視圖的關鍵字參數的字典。 view_args和view_kwargs都不包含第一個視圖參數(request)。
Django會在調用視圖函數以前調用process_view方法。
它應該返回None或一個HttpResponse對象。 若是返回None,Django將繼續處理這個請求,執行任何其餘中間件的process_view方法,而後在執行相應的視圖。 若是它返回一個HttpResponse對象,Django不會調用適當的視圖函數。 它將執行中間件的process_response方法並將應用到該HttpResponse並返回結果。
給MD1和MD2添加process_view方法:
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") pass 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__)
訪問index視圖函數,看一下輸出結果:
MD2裏面的 process_request MD1裏面的 process_request -------------------------------------------------------------------------------- MD2 中的process_view <function index at 0x000001DE68317488> index -------------------------------------------------------------------------------- MD1 中的process_view <function index at 0x000001DE68317488> index app01 中的 index視圖 MD1裏面的 process_response MD2裏面的 process_response
process_view方法是在process_request以後,視圖函數以前執行的,執行順序按照MIDDLEWARE中的註冊順序從前到後順序執行的
4 中間件process_exception
process_exception(self, request, exception)
該方法兩個參數:
一個HttpRequest對象
一個exception是視圖函數異常產生的Exception對象。
這個方法只有在視圖函數中出現異常了才執行,它返回的值能夠是一個None也能夠是一個HttpResponse對象。若是是HttpResponse對象,Django將調用模板和中間件中的process_response方法,並返回給瀏覽器,不然將默認處理異常。若是返回一個None,則交給下一個中間件的process_exception方法來處理異常。它的執行順序也是按照中間件註冊順序的倒序執行。
給MD1和MD2添加上這個方法:
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") pass 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")
若是視圖函數中無異常,process_exception方法不執行。
想辦法,在視圖函數中拋出一個異常:
def index(request): print("app01 中的 index視圖") raise ValueError("呵呵") return HttpResponse("O98K")
在MD1的process_exception中返回一個響應對象:
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)) # 返回一個響應對象
看輸出結果:
MD2裏面的 process_request MD1裏面的 process_request -------------------------------------------------------------------------------- MD2 中的process_view <function index at 0x0000022C09727488> index -------------------------------------------------------------------------------- MD1 中的process_view <function index at 0x0000022C09727488> index app01 中的 index視圖 呵呵 MD1 中的process_exception MD1裏面的 process_response MD2裏面的 process_response
注意,這裏並無執行MD2的process_exception方法,由於MD1中的process_exception方法直接返回了一個響應對象。
5 中間件process_template_response(self, request, response)
process_template_response(self, request, response)
它的參數,一個HttpRequest對象,response是TemplateResponse對象(由視圖函數或者中間件產生)。
process_template_response是在視圖函數執行完成後當即執行,可是它有一個前提條件,那就是視圖函數返回的對象有一個render()方法(或者代表該對象是一個TemplateResponse對象或等價方法)。
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") pass 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
views.py中:
def index(request): print("app01 中的 index視圖") def render(): print("in index/render") return HttpResponse("O98K") rep = HttpResponse("OK") rep.render = render return rep
訪問index視圖,終端輸出的結果:
MD2裏面的 process_request MD1裏面的 process_request -------------------------------------------------------------------------------- MD2 中的process_view <function index at 0x000001C111B97488> index -------------------------------------------------------------------------------- MD1 中的process_view <function index at 0x000001C111B97488> index app01 中的 index視圖 MD1 中的process_template_response MD2 中的process_template_response in index/render MD1裏面的 process_response MD2裏面的 process_response
從結果看出:
視圖函數執行完以後,當即執行了中間件的process_template_response方法,順序是倒序,先執行MD1的,在執行MD2的,接着執行了視圖函數返回的HttpResponse對象的render方法,返回了一個新的HttpResponse對象,接着執行中間件的process_response方法。
五大方法在Django中的執行流程:
上一部分,咱們瞭解了中間件中的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兩個方法的觸發是有條件的,執行順序也是倒序。總結全部的執行流程以下:
那麼咱們研究過中間件以後咱們知道可使用中間件控制咱們的登陸驗證:
中間件版的登陸驗證須要依靠session,因此數據庫中要有django_session表。
urls.py
from django.conf.urls import url from app01 import views urlpatterns = [ url(r'^index/$', views.index), url(r'^login/$', views.login, name='login'), ] urls.py
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 == "Q1mi" and pwd == "123456": # 設置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") views.py
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' %}"> <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> login.html
middlewares.py
class AuthMD(MiddlewareMixin): white_list = ['/login/', ] # 白名單 balck_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.white_list or request.session.get("user"): return elif next_url in self.balck_list: return HttpResponse('This is an illegal URL') 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', 'middlewares.AuthMD', ] settings.py中註冊中間件
AuthMD中間件註冊後,全部的請求都要走AuthMD的process_request方法。
訪問的URL在白名單內或者session中有user用戶名,則不作阻攔走正常流程;
若是URL在黑名單中,則返回This is an illegal URL的字符串;
正常的URL是須要登陸後訪問,讓瀏覽器跳轉到登陸頁面。
注:AuthMD中間件中須要session,因此AuthMD註冊的位置要在session中間的下方。
Django總體請求流程圖: