一、裝飾器
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()