Flask中特殊裝飾器的使用
Flask中用作裝飾器的特殊的函數(shù) 第一部分:before_request和after_request
在Flask中,before_request
和after_request
是用作裝飾器的特殊函數(shù),它們可以用來(lái)在請(qǐng)求處理過(guò)程中執(zhí)行某些操作。
一、 before_request裝飾器:
- 通過(guò)在函數(shù)上使用
@app.before_request
裝飾器,可以將該函數(shù)注冊(cè)為全局的請(qǐng)求前鉤子(hook)。這意味著每次請(qǐng)求到達(dá)服務(wù)器時(shí),在實(shí)際處理請(qǐng)求之前,都會(huì)先執(zhí)行被before_request
裝飾的函數(shù)。 before_request
函數(shù)通常用于執(zhí)行一些預(yù)處理任務(wù),例如驗(yàn)證用戶身份、設(shè)置全局變量、打開(kāi)數(shù)據(jù)庫(kù)連接等。它可以修改請(qǐng)求或應(yīng)用程序上下文,并且可以返回響應(yīng)對(duì)象或 None(后面扒扒源碼看看)。
比如:
@app.before_request def before_request(): # 執(zhí)行一些預(yù)處理任務(wù) if not current_user.is_authenticated: return redirect(url_for('login'))
二、after_request裝飾器:
- 通過(guò)在函數(shù)上使用
@app.after_request
裝飾器,可以將該函數(shù)注冊(cè)為全局的請(qǐng)求后鉤子。這意味著在每次請(qǐng)求完成并返回響應(yīng)之后,都會(huì)執(zhí)行被after_request
裝飾的函數(shù)。 after_request
函數(shù)通常用于執(zhí)行一些后處理任務(wù),例如**添加響應(yīng)頭、記錄請(qǐng)求日志、關(guān)閉數(shù)據(jù)庫(kù)連接等。**它接收一個(gè)參數(shù),即響應(yīng)對(duì)象,并且必須返回一個(gè)響應(yīng)對(duì)象。
比如:
@app.after_request def after_request(response): # 執(zhí)行一些后處理任務(wù) response.headers['X-Frame-Options'] = 'SAMEORIGIN' return response
通過(guò)使用before_request
和after_request
裝飾器,可以在請(qǐng)求的前后執(zhí)行一些共同的邏輯,從而實(shí)現(xiàn)全局的預(yù)處理和后處理操作。這樣可以避免在每個(gè)視圖函數(shù)中重復(fù)編寫(xiě)相同的代碼。
三、多個(gè)before_request和after_request執(zhí)行流程分析:
首先—理論講解:
當(dāng)存在多個(gè)before_request
和after_request
裝飾器時(shí),分析它們的執(zhí)行順序:
before_request
執(zhí)行流程:
- 當(dāng)一個(gè)請(qǐng)求到達(dá)服務(wù)器時(shí),首先會(huì)執(zhí)行第一個(gè)注冊(cè)的
before_request
裝飾的函數(shù)。 - 如果該函數(shù)返回了一個(gè)響應(yīng)對(duì)象,則停止執(zhí)行后續(xù)所有的
before_request
函數(shù),而是直接返回該響應(yīng)對(duì)象給客戶端。 - 如果該函數(shù)沒(méi)有返回響應(yīng)對(duì)象,則繼續(xù)執(zhí)行下一個(gè)注冊(cè)的
before_request
函數(shù),以此類推,直到所有的before_request
函數(shù)都被執(zhí)行完畢。
after_request
執(zhí)行流程:
- 在每次請(qǐng)求完成并返回響應(yīng)之后,從最后一個(gè)注冊(cè)的
after_request
裝飾的函數(shù)開(kāi)始執(zhí)行。 - 每個(gè)
after_request
函數(shù)都會(huì)接收前一個(gè)after_request
函數(shù)所返回的響應(yīng)對(duì)象作為參數(shù),并且必須返回一個(gè)新的響應(yīng)對(duì)象。 - 執(zhí)行完最后一個(gè)
after_request
函數(shù)后,最終的響應(yīng)對(duì)象將會(huì)發(fā)送給客戶端。
然后—實(shí)戰(zhàn)講解:
from flask import Flask, session app = Flask(__name__) # 創(chuàng)建Flask應(yīng)用程序?qū)ο? @app.before_request def before_request_1(): print("Before Request 1") @app.before_request def before_request_2(): print("Before Request 2") @app.after_request def after_request_1(response): print("After Request 1") return response @app.after_request def after_request_2(response): print("After Request 2") return response @app.route('/') def index(): print("Index Page") return "Hello, World!" if __name__ == '__main__': app.run()
輸出的執(zhí)行順序如下:
可以看到,首先執(zhí)行了before_request_1
和before_request_2
兩個(gè)函數(shù),然后處理了請(qǐng)求并返回響應(yīng),接著按照相反的順序執(zhí)行了after_request_2
和after_request_1
兩個(gè)函數(shù)。
最后—扒扒源碼:
為何before_request
是按注冊(cè)順序執(zhí)行,而after_request
是按注冊(cè)倒序執(zhí)行嘞?
扒一扒源碼就曉得啦~
直接進(jìn)入before_request
和after_request
的身體:
上面就是Flask注冊(cè)before_request
和after_request
函數(shù)的方法,一模一樣!
下面來(lái)扒下Flask執(zhí)行before_request
和after_request
函數(shù)部分的源碼:
進(jìn)入app.__call__()
后直到full_dispatch_request
函數(shù):
關(guān)注點(diǎn)先放在上圖第二個(gè)for循環(huán),其余部分后續(xù)會(huì)慢慢探究~
before_request_funcs
就是上述包含了請(qǐng)求前鉤子函數(shù)的字典。這些函數(shù)會(huì)在請(qǐng)求分發(fā)(即dispatch_request
)之前被調(diào)用。
遍歷每個(gè)注冊(cè)的請(qǐng)求前鉤子函數(shù),并執(zhí)行它們。如果任何一個(gè)請(qǐng)求前鉤子函數(shù)**返回一個(gè)非 None 的值,該值會(huì)被當(dāng)作視圖函數(shù)的返回值處理(直接返回給客戶端頁(yè)面),并且后續(xù)的請(qǐng)求處理流程會(huì)被停止【但是after_request正常執(zhí)行!】;**如果沒(méi)有任何請(qǐng)求前鉤子函數(shù)返回非 None 值,那么 preprocess_request
方法會(huì)返回 None。
來(lái)個(gè)代碼講解:
畫(huà)圖形象記憶:
綠色就是所有請(qǐng)求前鉤子函數(shù)(before_request
)返回值都為None時(shí)的執(zhí)行流程;
相應(yīng)的,白色就是有一個(gè)返回值非None時(shí)的執(zhí)行流程。
差點(diǎn)忘了帶大家看after_request
了!
看到?jīng)]!reversed?。。》聪虮闅v哦~
第二部分:實(shí)戰(zhàn)—使用before_request進(jìn)行身份驗(yàn)證
上代碼:
from flask import Flask, request, redirect, url_for, render_template, session app = Flask(__name__) # 模擬的用戶數(shù)據(jù)庫(kù) users = { 'admin': { 'username': 'admin', 'password': 'GuHanZhe' } } @app.before_request def authenticate_user(): # 獲取當(dāng)前請(qǐng)求的路徑 path = request.path # 如果請(qǐng)求的路徑不是登錄頁(yè)面,進(jìn)行身份驗(yàn)證 if path != '/login': # 檢查 session 中是否存在已登錄的用戶 if 'username' not in session: # 用戶未登錄,重定向到登錄頁(yè)面 return redirect(url_for('login')) @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] # 在實(shí)際開(kāi)發(fā)中,這里需要進(jìn)行密碼驗(yàn)證 if username in users and users[username]['password'] == password: # 登錄成功,將用戶名保存在 session 中 session['username'] = username return redirect(url_for('protected_page')) else: # 登錄失敗,顯示錯(cuò)誤信息 error_message = "Invalid username or password." return render_template('login.html', error_message=error_message) return render_template('login.html') @app.route('/protected') def protected_page(): return "This is a protected page. Only logged-in users can access it." if __name__ == '__main__': app.run()
在上述代碼中,before_request
鉤子函數(shù) authenticate_user
用于驗(yàn)證用戶身份。它會(huì)在每個(gè)請(qǐng)求到達(dá)之前被調(diào)用,除了登錄頁(yè)面 /login
外的所有頁(yè)面都需要進(jìn)行身份驗(yàn)證。
如果用戶未登錄,authenticate_user
函數(shù)將重定向到登錄頁(yè)面,使用 redirect
函數(shù)和 url_for
函數(shù)實(shí)現(xiàn)頁(yè)面重定向。登錄成功后,將用戶名保存在 session
中,以便在后續(xù)的請(qǐng)求中進(jìn)行驗(yàn)證。
login
路由處理函數(shù)負(fù)責(zé)渲染登錄頁(yè)面,并接收用戶提交的表單數(shù)據(jù)。在實(shí)際應(yīng)用中,需要根據(jù)數(shù)據(jù)庫(kù)中的用戶信息進(jìn)行密碼驗(yàn)證。驗(yàn)證成功后,將用戶名保存在 session
中,并重定向到受保護(hù)的頁(yè)面 /protected
。
protected_page
路由處理函數(shù)是一個(gè)示例的受保護(hù)頁(yè)面,只有登錄用戶可見(jiàn)。
第三部分:補(bǔ)充常見(jiàn)特殊裝飾器
一、@app.errorhandler(code):
- 這是用于注冊(cè)錯(cuò)誤處理函數(shù)的裝飾器。
code
參數(shù)指定了需要處理的錯(cuò)誤碼,例如 404、500 等。- 裝飾的函數(shù)將作為錯(cuò)誤處理函數(shù),在出現(xiàn)指定錯(cuò)誤碼時(shí)被調(diào)用,并返回自定義的錯(cuò)誤頁(yè)面或響應(yīng)。
二、 @app.teardown_request:
用于注冊(cè)在每個(gè)請(qǐng)求結(jié)束時(shí)執(zhí)行的函數(shù)。它可以用來(lái)進(jìn)行一些清理操作或釋放資源。
實(shí)戰(zhàn):
from flask import Flask app = Flask(__name__) @app.route('/') def index(): return "Hello, World!" @app.teardown_request def teardown_request_func(error=None): print("Teardown function is called after each request.") if __name__ == '__main__': app.run()
在上述代碼中,定義了一個(gè)名為 teardown_request_func
的函數(shù),并使用 @app.teardown_request
裝飾器將其注冊(cè)為每個(gè)請(qǐng)求結(jié)束時(shí)執(zhí)行的函數(shù)。
當(dāng)我們?cè)L問(wèn)任何路由時(shí),F(xiàn)lask 會(huì)在請(qǐng)求結(jié)束后自動(dòng)調(diào)用 teardown_request_func
函數(shù)。無(wú)論請(qǐng)求是否出現(xiàn)錯(cuò)誤,該函數(shù)都會(huì)被執(zhí)行。
需要注意的是,@app.teardown_request
裝飾的函數(shù)只能接受一個(gè)參數(shù),即可選的錯(cuò)誤對(duì)象。如果要訪問(wèn)請(qǐng)求上下文中的其他對(duì)象,可以使用 flask.request
對(duì)象。
這個(gè)裝飾器通常用于進(jìn)行一些清理操作,例如關(guān)閉數(shù)據(jù)庫(kù)連接、釋放資源等。
三、@app.template_test:
用于注冊(cè)自定義模板測(cè)試函數(shù)的裝飾器。
實(shí)戰(zhàn):
from flask import Flask app = Flask(__name__) @app.template_test('even') def is_even(number): return number % 2 == 0 if __name__ == '__main__': app.run()
通過(guò) @app.template_test('even')
裝飾器將 is_even
函數(shù)注冊(cè)為一個(gè)名為 “even” 的模板測(cè)試函數(shù)。該函數(shù)用于判斷一個(gè)數(shù)字是否是偶數(shù)。
在模板中,可以使用 {% if %}
語(yǔ)句來(lái)調(diào)用這個(gè)模板測(cè)試函數(shù):
{% if num is even %} <p>The number is even</p> {% else %} <p>The number is odd</p> {% endif %}
通過(guò)注冊(cè)模板測(cè)試函數(shù),我們可以在模板中使用自定義的邏輯判斷函數(shù),
以便根據(jù)特定的條件進(jìn)行動(dòng)態(tài)渲染和顯示不同的內(nèi)容。
四、@app.before_first_request:
用于注冊(cè)在第一個(gè)請(qǐng)求到達(dá)之前執(zhí)行的函數(shù)。它只會(huì)在應(yīng)用程序啟動(dòng)時(shí)執(zhí)行一次。
實(shí)戰(zhàn):
from flask import Flask app = Flask(__name__) @app.before_first_request def before_first_request_func(): print("This function is executed before the first request.") @app.route('/') def index(): return "Hello, World!" if __name__ == '__main__': app.run()
在上述代碼中,before_first_request_func
被裝飾為 @app.before_first_request
,它會(huì)在第一個(gè)請(qǐng)求到達(dá)之前執(zhí)行。
當(dāng)我們運(yùn)行這個(gè)應(yīng)用程序時(shí),before_first_request_func
函數(shù)會(huì)在第一個(gè)請(qǐng)求到達(dá)之前執(zhí)行一次。之后,每個(gè)請(qǐng)求到達(dá)時(shí),都不會(huì)再次調(diào)用該函數(shù)。
需要注意的是,@app.before_first_request
裝飾的函數(shù)僅在主線程中執(zhí)行,并且只有在應(yīng)用程序啟動(dòng)時(shí)才會(huì)被調(diào)用一次。如果使用多線程或多進(jìn)程部署應(yīng)用程序,可以考慮使用其他方法來(lái)進(jìn)行初始化操作。
【新版本Flask沒(méi)有這個(gè)裝飾器了~】
到此這篇關(guān)于Flask中特殊裝飾器的使用的文章就介紹到這了,更多相關(guān)Flask 特殊裝飾器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python實(shí)現(xiàn)協(xié)同過(guò)濾推薦算法完整代碼示例
這篇文章主要介紹了python實(shí)現(xiàn)協(xié)同過(guò)濾推薦算法完整代碼示例,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12spark: RDD與DataFrame之間的相互轉(zhuǎn)換方法
今天小編就為大家分享一篇spark: RDD與DataFrame之間的相互轉(zhuǎn)換方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-06-06Python使用正則表達(dá)式實(shí)現(xiàn)爬蟲(chóng)數(shù)據(jù)抽取
這篇文章主要介紹了Python使用正則表達(dá)式實(shí)現(xiàn)爬蟲(chóng)數(shù)據(jù)抽取,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08Python?sklearn?中的?make_blobs()?函數(shù)示例詳解
make_blobs()?是?sklearn.datasets中的一個(gè)函數(shù),這篇文章主要介紹了Python?sklearn?中的?make_blobs()?函數(shù),本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02python?pygame實(shí)現(xiàn)打磚塊游戲
這篇文章主要為大家詳細(xì)介紹了python?pygame實(shí)現(xiàn)打磚塊游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05Django-xadmin+rule對(duì)象級(jí)權(quán)限的實(shí)現(xiàn)方式
今天小編就為大家分享一篇Django-xadmin+rule對(duì)象級(jí)權(quán)限的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03pycharm新建Vue項(xiàng)目的方法步驟(圖文)
這篇文章主要介紹了pycharm新建Vue項(xiàng)目的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03