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