2018-12-03 裝飾器、中間件和session

一、裝飾器

1.1 什么是裝飾器

  • 裝飾器模式(Decorator Pattern)允許向一個(gè)現(xiàn)有的對(duì)象添加新的功能,同時(shí)又不改變其結(jié)構(gòu)。這種類(lèi)型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,它是作為現(xiàn)有的類(lèi)的一個(gè)包裝。

  • 這種模式創(chuàng)建了一個(gè)裝飾類(lèi),用來(lái)包裝原有的類(lèi),并在保持類(lèi)方法簽名完整性的前提下,提供了額外的功能。

1.2 裝飾器的三個(gè)基本點(diǎn)

    1、外層函數(shù)內(nèi)嵌內(nèi)層函數(shù)
    2、外層函數(shù)返回內(nèi)層函數(shù)
    3、內(nèi)層函數(shù)調(diào)用外層函數(shù)的參數(shù)

??例如:

# 定義登錄驗(yàn)證的裝飾器
def is_login(func):
    # func 就是被裝飾的函數(shù)(function類(lèi)型)
    def check_status(request):
        token = request.COOKIES.get('token')
        if token:
            token_user = TokenUser.objects.filter(token=token).first()
            if token_user:
                # 返回func(request)表示繼續(xù)執(zhí)行被裝飾的函數(shù)
                return func(request)
            else:
                return HttpResponseRedirect('/login/')
        else:
            return HttpResponseRedirect('/login/')

    return check_status

??注意:在上面的函數(shù)中外層函數(shù)的形參func指的是飾器裝飾的函數(shù),是一個(gè)function對(duì)象。return func(request)表示繼續(xù)執(zhí)行被裝飾函數(shù)(如果需要繼續(xù)執(zhí)行被裝飾函數(shù)就應(yīng)該這樣寫(xiě)),由于被裝飾的函數(shù)有一個(gè)形參request,所以需要傳入這個(gè)request參數(shù),根據(jù)具體函數(shù)情況而定,也可以其他的執(zhí)行操作。

?? 裝飾器優(yōu)點(diǎn):利用裝飾器可以替代我們純手寫(xiě)的登錄校驗(yàn)代碼,同時(shí)裝飾器有較高的靈活性,耦合性低,減少代碼重復(fù)。

?? 裝飾器缺點(diǎn):多層裝飾比較復(fù)雜。

二、中間件

2.1 什么是中間件:

??面向切面編程AOP中的中間件是一個(gè)最好的例子。官方的說(shuō)法:中間件是一個(gè)用來(lái)處理Django的請(qǐng)求和響應(yīng)的框架級(jí)別的鉤子。它是一個(gè)輕量、低級(jí)別的插件系統(tǒng),用于在全局范圍內(nèi)改變Django的輸入和輸出。

2.2 中間件Middleware描述

    中間件: 
        1. 是一個(gè)輕量級(jí)的,底層的插件,可以介入Django的請(qǐng)求和響應(yīng)的過(guò)程(面向切面編程)
        2. 中間件的本質(zhì)就是一個(gè)python類(lèi)

??注意:中間件是幫助我們?cè)谝晥D函數(shù)執(zhí)行之前和執(zhí)行之后都可以做一些額外的操作,它本質(zhì)上就是一個(gè)自定義類(lèi),類(lèi)中定義了幾個(gè)方法,Django框架會(huì)在請(qǐng)求的特定的時(shí)間去執(zhí)行這些方法。

2.3 中間件中的方法

?? 每個(gè)中間j件都是一個(gè)獨(dú)立的類(lèi),主要有一下幾個(gè)方法

    1. process_requeest(self, request)
        該方法是在Django接收到request之后,但仍未解析出url以確定運(yùn)行哪個(gè)視圖函數(shù)view視圖函數(shù)之前。
    2. process_view(self, request, view_func, view_args, view_kwargs)
        執(zhí)行時(shí)機(jī)在django執(zhí)行完request預(yù)處理函數(shù)并確定待執(zhí)行的view之后, 但在視圖函數(shù)view之前
        request: HttpRequest對(duì)象
        view_fun: 是django將要調(diào)用的視圖函數(shù), 是真實(shí)的函數(shù)對(duì)象本身
        view_args: 將傳入view的位置參數(shù)列表, 不包括request參數(shù)
        view_kwargs: 將傳入view的字典參數(shù)
    3. process_response(self, request, response)
        該方法必須返回HttpResponse對(duì)象, 可以是原來(lái)的, 也可以是修改后的
        調(diào)用時(shí)機(jī)在django執(zhí)行完view函數(shù)并生成response之后, 該中間件能修改response的內(nèi)容, 常見(jiàn)用途比如壓縮內(nèi)容
        request是request對(duì)象
        response是從view中返回的response對(duì)象
    4. process_exception(self, request, exception)
        默認(rèn)不主動(dòng)調(diào)用,該方法只有在request處理過(guò)程中出了問(wèn)題并且view函數(shù)拋出了一個(gè)未捕獲的異常才會(huì)被調(diào)用, 可以用來(lái)發(fā)送錯(cuò)誤通知, 將相關(guān)信息輸出到日志文件, 或者甚至嘗試從錯(cuò)誤中自動(dòng)恢復(fù)。
        參數(shù)包括request對(duì)象, 還有view函數(shù)拋出的異常對(duì)象exception
        必須返回None或HttpResponse對(duì)象
    5. process_template_response(self, request, response)
        默認(rèn)不主動(dòng)調(diào)用,在視圖執(zhí)行render()返回后進(jìn)行調(diào)用,必須返回None或HttpResponse對(duì)象

??注意:以上方法的返回值可以是None或一個(gè)HttpResponse對(duì)象,如果是None,則繼續(xù)按照django定義的規(guī)則向后繼續(xù)執(zhí)行,如果是HttpResponse對(duì)象,則直接將該對(duì)象返回給用戶(hù)。

?? 具體函數(shù)解釋?zhuān)?/strong>

    1. process_request()函數(shù)
    process_request方法中有一個(gè)request參數(shù),其表示請(qǐng)求。該方法中可以返回None或不用返回任何參數(shù),或返回HttpResponse對(duì)象。如果返回None或不返回任何參數(shù)則表示繼續(xù)執(zhí)行其余中間件,如果是返回HttpResponse對(duì)象則直接返回HttpResponse對(duì)象給客戶(hù)端,而不再執(zhí)行視圖函數(shù)。


    2. process_response(self, request, response)函數(shù)
    process_ response方法中兩個(gè)參數(shù),一個(gè)是請(qǐng)求request參數(shù),一個(gè)是響應(yīng)response參數(shù),該response參數(shù)就是視圖函數(shù)返回的HttpResponse對(duì)象。

?? 修改中間件TestMiddlware1和TestMiddlware2,修改代碼如下:

class TestMiddlware1(MiddlewareMixin):

    def process_request(self, request):
        print('test1 process_request')

    def process_response(self, request, response):
        print('test1 process_response')
        return response

class TestMiddlware2(MiddlewareMixin):

    def process_request(self, request):
        print('test2 process_request')

    def process_response(self, request, response):
        print('test2 process_response')
        return response

?? 訪問(wèn)inde路由地址,在控制臺(tái)中可以打印如下的內(nèi)容:

    test1 process_request
    test2 process_request
    index views
    test2 process_response
    test1 process_response

?? 從結(jié)果中可以發(fā)現(xiàn),中間件的process_request在訪問(wèn)視圖函數(shù)之前執(zhí)行,而process_reponse在視圖函數(shù)之后執(zhí)行。并且從執(zhí)行順序中可以得出以下結(jié)論:

    1)  多個(gè)中間件的process_request的執(zhí)行順序是按照在MIDDLEWARE中定義的先后順序執(zhí)行的。
    2)  多個(gè)中間件的process_response的執(zhí)行順序是按照MIDDLEWARE中定義的順序逆序執(zhí)行的。也就是說(shuō)第一個(gè)中間件的process_request先執(zhí)行,而第一個(gè)中間件的process_response最后執(zhí)行。
    3)  視圖函數(shù)在process_request之后執(zhí)行。
    4)  視圖函數(shù)在process_response之前執(zhí)行。
    5)  process_response必須返回響應(yīng)對(duì)象。


    3. process_view(self, view_func, view_args, view_kwargs) 講解及處理流程

?? 該方法接收四個(gè)參數(shù):

    請(qǐng)求request
    view_func: 即將被執(zhí)行的函數(shù)
    view_args:傳遞給視圖函數(shù)的列表參數(shù)
    view_kwargs:傳遞給視圖函數(shù)的字典參數(shù)

?? 修改中間件TestMiddlware1和TestMiddlware2,修改代碼如下:

class TestMiddlware1(MiddlewareMixin):

    def process_request(self, request):
        print('test1 process_request')

    def process_response(self, request, response):
        print('test1 process_response')
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print('test1 process_view')

class TestMiddlware2(MiddlewareMixin):

    def process_request(self, request):
        print('test2 process_request')

    def process_response(self, request, response):
        print('test2 process_response')
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print('test2 process_view')

?? 訪問(wèn)index路由地址,在控制臺(tái)中可以打印如下的內(nèi)容:

    test1 process_request
    test2 process_request
    test1 process_view
    test2 process_view
    index views
    test2 process_response
    test1 process_response

?? 從結(jié)果中可以發(fā)現(xiàn),中間件中的process_view方法在視圖函數(shù)之前執(zhí)行,在process_request方法之后執(zhí)行,process_view執(zhí)行的順序按照MIDDLEWARE中定義中間件的順序執(zhí)行的。并且從執(zhí)行的結(jié)果中可以得出以下結(jié)論:

    1)  process_request執(zhí)行后才執(zhí)行process_view
    2)  視圖函數(shù)在process_view方法執(zhí)行后執(zhí)行
    3)  process_view方法在process_response方法之后執(zhí)行,并且執(zhí)行的順序按照MIDDLEWARE中定義中間件的順序執(zhí)行

    4. process_template_response(self, request, response) 講解及處理流程
    該方法中接收兩個(gè)參數(shù),一個(gè)是請(qǐng)求request,一個(gè)是響應(yīng)response,該響應(yīng)response由視圖函數(shù)產(chǎn)生。process_template_response方法默認(rèn)是不執(zhí)行的,只會(huì)在視圖函數(shù)返回對(duì)象有一個(gè)render方法時(shí)才會(huì)被調(diào)用。

?? 修改index視圖函數(shù)

    rep = HttpResponse()
    rep.render = index_render
    return rep

?? 修改中間件TestMiddlware1和TestMiddlware2,修改代碼如下:

class TestMiddlware1(MiddlewareMixin):

    def process_request(self, request):
        print('test1 process_request')

    def process_response(self, request, response):
        print('test1 process_response')
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print('test1 process_view')

    def process_exception(self, request, exception):
        print('test1 process_except')

    def process_template_response(self, request, response):
        print('test1 process_template_response')
        return response

class TestMiddlware2(MiddlewareMixin):

    def process_request(self, request):
        print('test2 process_request')

    def process_response(self, request, response):
        print('test2 process_response')
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print('test2 process_view')

    def process_exception(self, request, exception):
        print('test2 process_except')

    def process_template_response(self, request, response):
        print('test2 process_template_response')
        return response

?? 訪問(wèn)index路由地址,在控制臺(tái)中可以打印如下的內(nèi)容:

    test1 process_request
    test2 process_request
    test1 process_view
    test2 process_view
    index views
    test2 process_template_response
    test1 process_template_response
    test2 process_response
    test1 process_response

?? 從結(jié)果中可以得出以下結(jié)論:

    1)  process_template_response在視圖函數(shù)執(zhí)行完后。并且執(zhí)行的順序按照MIDDLEWARE中定義中間件的順序逆序執(zhí)行。
    2)  process_response方法是最后執(zhí)行的,并且執(zhí)行的順序按照MIDDLEWARE中定義中間件的順序逆序執(zhí)行。

    5. process_exception(self, request, exception) 講解及處理流程
    該方法中接收兩個(gè)參數(shù),一個(gè)是請(qǐng)求request,一個(gè)是異常exception,該exception是視圖函數(shù)產(chǎn)生的異常Exception對(duì)象。process_exception方法默認(rèn)是不執(zhí)行的,只會(huì)在視圖函數(shù)出現(xiàn)異常的情況才會(huì)執(zhí)行。

?? 修改index視圖函數(shù),使得index方法拋出一個(gè)異常:

def index(request):
    if request.method == 'GET':
        print('index views')
        1/0
        return HttpResponse('我是index方法')

?? 修改中間件TestMiddlware1和TestMiddlware2,修改代碼如下:

class TestMiddlware1(MiddlewareMixin):

    def process_request(self, request):
        print('test1 process_request')

    def process_response(self, request, response):
        print('test1 process_response')
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print('test1 process_view')

    def process_exception(self, request, exception):
        print('test1 process_except')

class TestMiddlware2(MiddlewareMixin):

    def process_request(self, request):
        print('test2 process_request')

    def process_response(self, request, response):
        print('test2 process_response')
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print('test2 process_view')

    def process_exception(self, request, exception):
        print('test2 process_except')

?? 訪問(wèn)index路由地址,在控制臺(tái)中可以打印如下的內(nèi)容:

    test1 process_request
    test2 process_request
    test1 process_view
    test2 process_view
    index views
    test2 process_except
    test1 process_except
    test2 process_response
    test1 process_response

?? 從結(jié)果中可以得出以下結(jié)論:

    1)  process_request最先執(zhí)行。并且執(zhí)行的順序按照MIDDLEWARE中定義中間件的順序執(zhí)行。
    2)  process_view在視圖函數(shù)之前執(zhí)行,并且執(zhí)行的順序按照MIDDLEWARE中定義中間件的順序執(zhí)行。
    3)  視圖函數(shù)在process_view方法執(zhí)行后執(zhí)行,在process_exception方法之前執(zhí)行。
    4)  process_exception方法在process_response方法之前執(zhí)行,并且執(zhí)行的順序按照MIDDLEWARE中定義中間件的順序逆序執(zhí)行。
    5)  process_response方法是最后執(zhí)行的,并且執(zhí)行的順序按照MIDDLEWARE中定義中間件的順序逆序執(zhí)行。

三、cookie + session實(shí)現(xiàn)登錄的保持

?? 在為引入裝飾器和中間件之前,對(duì)于登錄狀態(tài)的保持我們都是自己在需要進(jìn)行登錄校驗(yàn)的視圖函數(shù)中添加登錄校驗(yàn)代碼,采用的是cookie的形式。這樣會(huì)產(chǎn)生大量的重復(fù)代碼,所以我們引入中間和裝飾器,減少代碼重復(fù)。

    由于cookie這種保持登錄狀態(tài)存在安全隱患,所以下面我們才有session和csrf進(jìn)行登錄狀態(tài)保持

    session指的是回話,是指當(dāng)用戶(hù)請(qǐng)求時(shí)服務(wù)端和客戶(hù)端會(huì)建立一個(gè)回話連接,當(dāng)用戶(hù)發(fā)送其他請(qǐng)求時(shí)會(huì)攜帶服務(wù)器端返回的session_key(一串隨機(jī)數(shù))來(lái)發(fā)送請(qǐng)求,服務(wù)端去Django維持的Django_session數(shù)據(jù)庫(kù)表中查詢(xún)是否有該key所對(duì)應(yīng)的數(shù)據(jù),有則允許訪問(wèn),否則不能訪問(wèn)。

?? 使用request.session向session中寫(xiě)入session值

    # 使用session實(shí)現(xiàn)登錄操作
    # 1. 向cookie中設(shè)置session值,value為隨機(jī)字符串
    # 2. 向Django_session表中存入sessionid值

        request.session['user_id'] = user.id

?? 這里設(shè)置的session值一般為主鍵,因?yàn)槲覀兛梢酝ㄟ^(guò)主鍵獲取到一個(gè)user對(duì)象,方便與我們后面取值,推薦使用。

?? 結(jié)合中間件或者裝飾器我們可以減少跟多的代碼,下面是采用中間件進(jìn)行登錄校驗(yàn)

class LoginStatusMiddleware(MiddlewareMixin):

    def process_request(self, request):
        # 在訪問(wèn)登錄和注冊(cè)頁(yè)面的時(shí)候都不需要進(jìn)行登錄校驗(yàn)
        if request.path in ['/login/', 'redister']:
            return None

        # 這個(gè)函數(shù)的return可寫(xiě)可不寫(xiě),推薦不寫(xiě)
        # 登錄校驗(yàn),當(dāng)訪問(wèn)需要登錄才能訪問(wèn)的頁(yè)面時(shí)就需要進(jìn)行登錄校驗(yàn),登錄了就能訪問(wèn),未登錄就不能訪問(wèn)
        # token校驗(yàn)
        # token = request.COOKIES.get('token')
        # if token:
        #     token_user = TokenUser.objects.filter(token=token).first()
        #     if not token_user:
        #         return HttpResponseRedirect('/login/')
        # else:
        #     return HttpResponseRedirect('/login/')
        # session校驗(yàn)
        # 1. 獲取cookie中的值,
        # 2. 查詢(xún)Django_session表中的session_key字段
        # 查詢(xún)大數(shù)據(jù),則獲取session_data中存入的鍵值對(duì)

        user_id = request.session.get('user_id')
        if user_id:
            user = MyUser.objects.get(pk=user_id)
            request.user = user
            return None
        else:
            return HttpResponseRedirect('/login/')

    def process_response(self, request, response):
        print('reponse')
        return response

?? 在上面定義的中間件類(lèi)中我們通過(guò)request.sessions.get('user_id')進(jìn)行session校驗(yàn),如果用戶(hù)登錄就能在Django_session表中獲取到相應(yīng)的session數(shù)據(jù),這樣一句就相當(dāng)于我們上面寫(xiě)的token檢驗(yàn)等功能,但是代碼量更少,可讀性跟高。裝飾器同樣的方式即可。

四、session的常用操作

1. 設(shè)置session

    session是鍵值對(duì)的形式存儲(chǔ)的,所以可以用以下方法設(shè)置session
    request.session['鍵名'] = value
    如: request.session['user_id'] = user.id

2. 獲取session值

    通過(guò)request.session.get()獲取session值
    格式: request.session.get(key)
    如: request.session.get('user_id')

3. 刪除session值

    刪除Django_session表中的值
    # 僅僅只刪除Django_session表中的數(shù)據(jù)
    request.session.delete(request.session.session_key)
    # 刪除客戶(hù)端和服務(wù)端session表中的值
    # request.session.flush()
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 利用HTTP協(xié)議向服務(wù)器傳參的幾種途徑、響應(yīng)、Cookie、Session、類(lèi)視圖、中間件 注意: 1>Dja...
    Cestine閱讀 1,512評(píng)論 0 2
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,695評(píng)論 19 139
  • 結(jié)果平等不可能即使可能也會(huì)造成社會(huì)效率低下,人的主觀能動(dòng)性下降,主觀能動(dòng)性下降必然導(dǎo)致社會(huì)協(xié)作低下。結(jié)果平等的初衷...
    MaleMonkey閱讀 4,468評(píng)論 0 0
  • 寒假伊始,針對(duì)出現(xiàn)的家庭問(wèn)題召開(kāi)三人緊急家庭會(huì)議。 女主人發(fā)言: 為度過(guò)一個(gè)和諧、和睦、團(tuán)結(jié)、舒心...
    風(fēng)吹過(guò)的心情ym閱讀 673評(píng)論 0 0
  • 經(jīng)家委會(huì)精心組織,8月5日上午,實(shí)驗(yàn)小學(xué)2015級(jí)7班小燕子中隊(duì)的部分同學(xué)在老師和家長(zhǎng)的帶領(lǐng)下,來(lái)到期待已...
    夏日涼爽閱讀 1,043評(píng)論 0 3

友情鏈接更多精彩內(nèi)容