16-djongo中间件学习
目錄
前戲
我們?cè)谇懊娴恼n程中已經(jīng)學(xué)會(huì)了給視圖函數(shù)加裝飾器來判斷是用戶是否登錄,把沒有登錄的用戶請(qǐng)求跳轉(zhuǎn)到登錄頁面。我們通過給幾個(gè)特定視圖函數(shù)加裝飾器實(shí)現(xiàn)了這個(gè)需求。但是以后添加的視圖函數(shù)可能也需要加上裝飾器,這樣是不是稍微有點(diǎn)繁瑣。
學(xué)完今天的內(nèi)容之后呢,我們就可以用更適宜的方式來實(shí)現(xiàn)類似給所有請(qǐng)求都做相同操作的功能了。
中間件
中間件介紹
什么是中間件?
官方的說法:中間件是一個(gè)用來處理Django的請(qǐng)求和響應(yīng)的框架級(jí)別的鉤子。它是一個(gè)輕量、低級(jí)別的插件系統(tǒng),用于在全局范圍內(nèi)改變Django的輸入和輸出。每個(gè)中間件組件都負(fù)責(zé)做一些特定的功能。
但是由于其影響的是全局,所以需要謹(jǐn)慎使用,使用不當(dāng)會(huì)影響性能。
說的直白一點(diǎn)中間件是幫助我們?cè)谝晥D函數(shù)執(zhí)行之前和執(zhí)行之后都可以做一些額外的操作,它本質(zhì)上就是一個(gè)自定義類,類中定義了幾個(gè)方法,Django框架會(huì)在處理請(qǐng)求的特定的時(shí)間去執(zhí)行這些方法。
我們一直都在使用中間件,只是沒有注意到而已,打開Django項(xiàng)目的Settings.py文件,看到下圖的MIDDLEWARE配置項(xiàng)。
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', ]MIDDLEWARE配置項(xiàng)是一個(gè)列表,列表中是一個(gè)個(gè)字符串,這些字符串其實(shí)是一個(gè)個(gè)類,也就是一個(gè)個(gè)中間件。
我們之前已經(jīng)接觸過一個(gè)csrf相關(guān)的中間件了?我們一開始讓大家把他注釋掉,再提交post請(qǐng)求的時(shí)候,就不會(huì)被forbidden了,后來學(xué)會(huì)使用csrf_token之后就不再注釋這個(gè)中間件了。
那接下來就學(xué)習(xí)中間件中的方法以及這些方法什么時(shí)候被執(zhí)行。
自定義中間件
中間件可以定義五個(gè)方法,分別是:(主要的是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)
以上方法的返回值可以是None或一個(gè)HttpResponse對(duì)象,如果是None,則繼續(xù)按照django定義的規(guī)則向后繼續(xù)執(zhí)行,如果是HttpResponse對(duì)象,則直接將該對(duì)象返回給用戶。
自定義一個(gè)中間件示例
from django.utils.deprecation import MiddlewareMixinclass MD1(MiddlewareMixin):def process_request(self, request):print("MD1里面的 process_request")def process_response(self, request, response):print("MD1里面的 process_response")return responseprocess_request
process_request有一個(gè)參數(shù),就是request,這個(gè)request和視圖函數(shù)中的request是一樣的。
它的返回值可以是None也可以是HttpResponse對(duì)象。返回值是None的話,按正常流程繼續(xù)走,交給下一個(gè)中間件處理,如果是HttpResponse對(duì)象,Django將不執(zhí)行視圖函數(shù),而將響應(yīng)對(duì)象返回給瀏覽器。
我們來看看多個(gè)中間件時(shí),Django是如何執(zhí)行其中的process_request方法的。
from django.utils.deprecation import MiddlewareMixinclass 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配置項(xiàng)中注冊(cè)上述兩個(gè)自定義中間件:
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 ]此時(shí),我們?cè)L問一個(gè)視圖,會(huì)發(fā)現(xiàn)終端中打印如下內(nèi)容:
MD1里面的 process_request MD2里面的 process_requestapp01 中的 index視圖
把MD1和MD2的位置調(diào)換一下,再訪問一個(gè)視圖,會(huì)發(fā)現(xiàn)終端中打印的內(nèi)容如下:
MD2里面的 process_request MD1里面的 process_requestapp01 中的 index視圖
看結(jié)果我們知道:視圖函數(shù)還是最后執(zhí)行的,MD2比MD1先執(zhí)行自己的process_request方法。
在打印一下兩個(gè)自定義中間件中process_request方法中的request參數(shù),會(huì)發(fā)現(xiàn)它們是同一個(gè)對(duì)象。
由此總結(jié)一下:
?
process_response
它有兩個(gè)參數(shù),一個(gè)是request,一個(gè)是response,request就是上述例子中一樣的對(duì)象,response是視圖函數(shù)返回的HttpResponse對(duì)象。該方法的返回值也必須是HttpResponse對(duì)象。
給上述的M1和M2加上process_response方法:
from django.utils.deprecation import MiddlewareMixinclass MD1(MiddlewareMixin):def process_request(self, request):print("MD1里面的 process_request")def process_response(self, request, response):print("MD1里面的 process_response")return responseclass MD2(MiddlewareMixin):def process_request(self, request):print("MD2里面的 process_request")def process_response(self, request, response):print("MD2里面的 process_response")return response訪問一個(gè)視圖,看一下終端的輸出:
MD2里面的 process_request MD1里面的 process_request app01 中的 index視圖 MD1里面的 process_response MD2里面的 process_response看結(jié)果可知:
process_response方法是在視圖函數(shù)之后執(zhí)行的,并且順序是MD1比MD2先執(zhí)行。(此時(shí)settings.py中 MD2比MD1先注冊(cè))
多個(gè)中間件中的process_response方法是按照MIDDLEWARE中的注冊(cè)順序倒序執(zhí)行的,也就是說第一個(gè)中間件的process_request方法首先執(zhí)行,而它的process_response方法最后執(zhí)行,最后一個(gè)中間件的process_request方法最后一個(gè)執(zhí)行,它的process_response方法是最先執(zhí)行。
?
process_view
process_view(self, request, view_func, view_args, view_kwargs)
該方法有四個(gè)參數(shù)
request是HttpRequest對(duì)象。
view_func是Django即將使用的視圖函數(shù)。 (它是實(shí)際的函數(shù)對(duì)象,而不是函數(shù)的名稱作為字符串。)
view_args是將傳遞給視圖的位置參數(shù)的列表.
view_kwargs是將傳遞給視圖的關(guān)鍵字參數(shù)的字典。 view_args和view_kwargs都不包含第一個(gè)視圖參數(shù)(request)。
Django會(huì)在調(diào)用視圖函數(shù)之前調(diào)用process_view方法。
它應(yīng)該返回None或一個(gè)HttpResponse對(duì)象。 如果返回None,Django將繼續(xù)處理這個(gè)請(qǐng)求,執(zhí)行任何其他中間件的process_view方法,然后在執(zhí)行相應(yīng)的視圖。 如果它返回一個(gè)HttpResponse對(duì)象,Django不會(huì)調(diào)用適當(dāng)?shù)囊晥D函數(shù)。 它將執(zhí)行中間件的process_response方法并將應(yīng)用到該HttpResponse并返回結(jié)果。
?給MD1和MD2添加process_view方法:
from django.utils.deprecation import MiddlewareMixinclass MD1(MiddlewareMixin):def process_request(self, request):print("MD1里面的 process_request")def process_response(self, request, response):print("MD1里面的 process_response")return responsedef 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 responsedef process_view(self, request, view_func, view_args, view_kwargs):print("-" * 80)print("MD2 中的process_view")print(view_func, view_func.__name__)訪問index視圖函數(shù),看一下輸出結(jié)果:
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_responseprocess_view方法是在process_request之后,視圖函數(shù)之前執(zhí)行的,執(zhí)行順序按照MIDDLEWARE中的注冊(cè)順序從前到后順序執(zhí)行的
process_exception
process_exception(self, request, exception)
該方法兩個(gè)參數(shù):
一個(gè)HttpRequest對(duì)象
一個(gè)exception是視圖函數(shù)異常產(chǎn)生的Exception對(duì)象。
這個(gè)方法只有在視圖函數(shù)中出現(xiàn)異常了才執(zhí)行,它返回的值可以是一個(gè)None也可以是一個(gè)HttpResponse對(duì)象。如果是HttpResponse對(duì)象,Django將調(diào)用模板和中間件中的process_response方法,并返回給瀏覽器,否則將默認(rèn)處理異常。如果返回一個(gè)None,則交給下一個(gè)中間件的process_exception方法來處理異常。它的執(zhí)行順序也是按照中間件注冊(cè)順序的倒序執(zhí)行。
?給MD1和MD2添加上這個(gè)方法:
from django.utils.deprecation import MiddlewareMixinclass MD1(MiddlewareMixin):def process_request(self, request):print("MD1里面的 process_request")def process_response(self, request, response):print("MD1里面的 process_response")return responsedef 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 responsedef 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")如果視圖函數(shù)中無異常,process_exception方法不執(zhí)行。
想辦法,在視圖函數(shù)中拋出一個(gè)異常:
def index(request):print("app01 中的 index視圖")raise ValueError("呵呵")return HttpResponse("O98K")在MD1的process_exception中返回一個(gè)響應(yīng)對(duì)象:
class MD1(MiddlewareMixin):def process_request(self, request):print("MD1里面的 process_request")def process_response(self, request, response):print("MD1里面的 process_response")return responsedef 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)) # 返回一個(gè)響應(yīng)對(duì)象看輸出結(jié)果:
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注意,這里并沒有執(zhí)行MD2的process_exception方法,因?yàn)镸D1中的process_exception方法直接返回了一個(gè)響應(yīng)對(duì)象。
process_template_response(用的比較少)
process_template_response(self, request, response)
它的參數(shù),一個(gè)HttpRequest對(duì)象,response是TemplateResponse對(duì)象(由視圖函數(shù)或者中間件產(chǎn)生)。
process_template_response是在視圖函數(shù)執(zhí)行完成后立即執(zhí)行,但是它有一個(gè)前提條件,那就是視圖函數(shù)返回的對(duì)象有一個(gè)render()方法(或者表明該對(duì)象是一個(gè)TemplateResponse對(duì)象或等價(jià)方法)。
class MD1(MiddlewareMixin):def process_request(self, request):print("MD1里面的 process_request")def process_response(self, request, response):print("MD1里面的 process_response")return responsedef 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 responseclass MD2(MiddlewareMixin):def process_request(self, request):print("MD2里面的 process_request")def process_response(self, request, response):print("MD2里面的 process_response")return responsedef 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 responseviews.py中:
def index(request):print("app01 中的 index視圖")def render():print("in index/render")return HttpResponse("O98K")rep = HttpResponse("OK")rep.render = renderreturn rep訪問index視圖,終端輸出的結(jié)果:
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從結(jié)果看出:
視圖函數(shù)執(zhí)行完之后,立即執(zhí)行了中間件的process_template_response方法,順序是倒序,先執(zhí)行MD1的,在執(zhí)行MD2的,接著執(zhí)行了視圖函數(shù)返回的HttpResponse對(duì)象的render方法,返回了一個(gè)新的HttpResponse對(duì)象,接著執(zhí)行中間件的process_response方法。
中間件的執(zhí)行流程
上一部分,我們了解了中間件中的5個(gè)方法,它們的參數(shù)、返回值以及什么時(shí)候執(zhí)行,現(xiàn)在總結(jié)一下中間件的執(zhí)行流程。
請(qǐng)求到達(dá)中間件之后,先按照正序執(zhí)行每個(gè)注冊(cè)中間件的process_reques方法,process_request方法返回的值是None,就依次執(zhí)行,如果返回的值是HttpResponse對(duì)象,不再執(zhí)行后面的process_request方法,而是執(zhí)行當(dāng)前對(duì)應(yīng)中間件的process_response方法,將HttpResponse對(duì)象返回給瀏覽器。也就是說:如果MIDDLEWARE中注冊(cè)了6個(gè)中間件,執(zhí)行過程中,第3個(gè)中間件返回了一個(gè)HttpResponse對(duì)象,那么第4,5,6中間件的process_request和process_response方法都不執(zhí)行,順序執(zhí)行3,2,1中間件的process_response方法。
?
process_request方法都執(zhí)行完后,匹配路由,找到要執(zhí)行的視圖函數(shù),先不執(zhí)行視圖函數(shù),先執(zhí)行中間件中的process_view方法,process_view方法返回None,繼續(xù)按順序執(zhí)行,所有process_view方法執(zhí)行完后執(zhí)行視圖函數(shù)。假如中間件3 的process_view方法返回了HttpResponse對(duì)象,則4,5,6的process_view以及視圖函數(shù)都不執(zhí)行,直接從最后一個(gè)中間件,也就是中間件6的process_response方法開始倒序執(zhí)行。
process_template_response和process_exception兩個(gè)方法的觸發(fā)是有條件的,執(zhí)行順序也是倒序。總結(jié)所有的執(zhí)行流程如下:
?
中間件版登錄驗(yàn)證?
中間件版的登錄驗(yàn)證需要依靠session,所以數(shù)據(jù)庫中要有django_session表。
urls.py
from django.conf.urls import url from django.contrib import admin from app01 import viewsurlpatterns = [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'), ]urls.py View Codeviews.py
from django.shortcuts import render, HttpResponse, redirectdef 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":# 設(shè)置sessionrequest.session["user"] = user# 獲取跳到登陸頁面之前的URLnext_url = request.GET.get("next")# 如果有,就跳轉(zhuǎn)回登陸之前的URLif next_url:return redirect(next_url)# 否則默認(rèn)跳轉(zhuǎn)到index頁面else:return redirect("/index/")return render(request, "login.html") views.pylogin.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> login.htmlmiddlewares.py
from django.utils.deprecation import MiddlewareMixinclass AuthMD(MiddlewareMixin):white_list = ['/login/', ] # 白名單black_list = ['/black/', ] # 黑名單def process_request(self, request):from django.shortcuts import redirect, HttpResponsenext_url = request.path_infoprint(request.path_info, request.get_full_path())# 黑名單的網(wǎng)址限制訪問if next_url in self.black_list:return HttpResponse('This is an illegal URL')# 白名單的網(wǎng)址或者登陸用戶不做限制elif next_url in self.white_list or request.session.get("user"):returnelse:return redirect("/login/?next={}".format(next_url))中間件 middlewares.py在settings.py中注冊(cè)
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' ] 注冊(cè)中間件AuthMD中間件注冊(cè)后,所有的請(qǐng)求都要走AuthMD的process_request方法。
如果URL在黑名單中,則返回This is an illegal URL的字符串;
訪問的URL在白名單內(nèi)或者session中有user用戶名,則不做阻攔走正常流程;
正常的URL但是需要登錄后訪問,讓瀏覽器跳轉(zhuǎn)到登錄頁面。
注:AuthMD中間件中需要session,所以AuthMD注冊(cè)的位置要在session中間的下方。?
附:Django請(qǐng)求流程圖
轉(zhuǎn)載于:https://www.cnblogs.com/bai-max/p/9355282.html
總結(jié)
以上是生活随笔為你收集整理的16-djongo中间件学习的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Unexpected end of JS
- 下一篇: 2018暑假集训---递推递归----一