亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

Flask 的鉤子函數使用

Flask 框架提供了鉤子函數的機制用于在特定的位置執行用戶注冊的函數,本小節講解了常見的鉤子函數的使用。

1. 鉤子簡介

1.1 什么是鉤子

在服務端處理請求時,有時需要:在處理請求前執行一些準備工作,在處理請求后執行一些掃尾工作,例如:

@app.route('/page1')
def page1():
    執行準備工作
    執行請求
    執行掃尾工作

@app.route('/page2')
def page2():
    執行準備工作
    執行請求
    執行掃尾工作

程序包括有 2 個頁面:page1 和 page2,在每個頁面處理函數的頭部,執行準備工作;在每個頁面處理函數的尾部,執行掃尾工作。

以上程序存在的明顯的代碼重復問題,為了讓每個頁面處理函數避免編寫重復功能的代碼,Flask 提供了鉤子函數機制:Flask 給應用程序提供掛載點,在掛載點調用應用程序注冊的函數,該函數被稱為鉤子函數。

1.2 注冊鉤子

Flask 框架處理請求時,提供了 2 個掛載點:before_request 和 after_request,執行請求的流程如下:

  1. 執行在掛載點 before_request 注冊的鉤子函數;
  2. 執行頁面處理函數;
  3. 執行在掛載點 after_request 注冊的鉤子函數。

Flask 使用裝飾器的語法注冊鉤子函數,如下所示:

@app.before_request
def before_request():
    print('before request')

使用裝飾器 @app.before_request 在掛載點 before_request 注冊函數 before_request,Flask 在處理每個請求時:

  • 首先調用函數 before_request,打印字符串 ‘before request’;
  • 然后再執行請求的處理函數。

1.3 消除代碼重復

下面使用鉤子函數機制消除 1.1 小節的代碼重復:

@app.before_request
def before_request():
    執行準備工作

@app.after_request
def after_request():
    執行掃尾工作

@app.route('/page1')
def page1():
    執行請求

@app.route('/page2')
def page2():
    執行請求

在第 1 行,在掛載點 before_request 注冊處理函數,執行請求前會調用該函數執行準備工作;在第 5 行,在掛載點 after_request 注冊處理函數,執行請求前后調用該函數執行掃尾工作。

與 1.1 小節中的代碼相比,頁面 /page1 和頁面 /page2 的處理函數得到了簡化,準備工作和掃尾工作被統一放置在鉤子函數中進行。

2. 常用的鉤子

Flask 框架中常用的鉤子總結如下:

1. before_first_request
在應用程序實例的第一個請求之前要運行的函數,只會運行一次。

2. before_request
在每個請求之前要運行的函數,對每一次請求都會執行一次。

3. after_request
在每個請求之后要運行的函數,對每一次請求都會執行一次。

在 after_request 注冊的鉤子函數的第一個參數是 response 對象。

4. teardown_request
在每個請求之后要運行的函數,對每一次請求都會執行一次。

在 teardown_request 注冊的鉤子函數的第一個參數是 exception 對象,指向執行請求時發生的錯誤。

5. errorhandler
發生一些異常時,比如 HTTP 404、HTTP 500 錯誤,或者拋出異常,就會自動調用該鉤子函數。

3. 鉤子的基本使用

3.1 程序代碼 basis.py

編寫一個程序 basis.py 分別在處理請求前、處理請求后注冊鉤子函數:

from flask import Flask, request, render_template

app = Flask(__name__)

@app.route('/')
def index():
    print('execute request')
    return 'Hello World'

@app.before_first_request
def before_first_request():
    print('before_first_request')

@app.before_request
def before_request():
    print('before_request')

@app.after_request
def after_request(response):
    print('after_request')
    return response

if __name__ == '__main__':
    app.run()

在第 10 行,通過 @app.before_first_request 注冊在第一次處理請求時執行的鉤子函數,該函數打印字符串 ‘before_first_request’。

在第 14 行,通過 @app.before_request 注冊在每次處理請求前執行的鉤子函數,該函數打印字符串 ‘before_request’。

在第 18 行,通過 @app.after_request 注冊在每次處理請求后執行的鉤子函數,該函數打印字符串 ‘after_request’。鉤子函數 after_request(response) 的輸入參數是一個 Response 對象,函數返回的也是一個 Response 對象。

3.2 運行程序 basis.py

1. 啟動程序

$ python basis.py

2. 在瀏覽器中首次訪問 http://localhost:5000

3. 觀察控制臺的輸出

before_first_request
before_request
execute request
after_request

應用程序在第一個請求之前要運行一次 before_first_request,只會運行一次。

4. 在瀏覽器中再次訪問 http://localhost:5000

5. 觀察控制臺的輸出

before_request
execute request
after_request

在程序整個生命周期內,before_first_request,只會運行一次,因此再次訪問網站時,不會再執行 before_first_request。

4. after_request 與 teardown_request 的區別

4.1 程序代碼 diff.py

本節通過一個具體的例子講解 after_request 與 teardown_request 的區別,代碼如下:

from flask import Flask, request, render_template

app = Flask(__name__)

@app.route('/')
def index():
    print('execute request without error')
    return 'Hello World'

@app.route('/error')
def error():
    print('execute request with error')
    1 / 0
    return 'Hello World'

@app.after_request
def after_request(response):
    print('after_request')
    return response

@app.teardown_request
def teardown_request(exception):
    print('teardown_request:', exception)

if __name__ == '__main__':
    app.run()

在第 5 行,訪問頁面 / 時執行函數 index(),該函數打印字符串 ‘execute request without error’,執行期間沒有發生異常。

在第 10 行,訪問頁面 /error 時執行函數 error(),該函數打印字符串 ‘execute request with error’,執行期間發生異常。在第 13 行,人為的通過 1 / 0 引發除零異常。

在第 16 行,注冊 after_request 鉤子函數,執行請求后會調用該鉤子函數。

在第 21 行,注冊 teardown_request 鉤子函數,執行請求后都會調用該鉤子函數。鉤子函數的第一個參數是 exception 對象,指向執行請求時發生的錯誤。

4.2 運行程序 diff.py

1. 啟動程序 diff.py

$ python diff.py

2. 在瀏覽器中訪問 http://localhost:5000

3. 觀察控制臺的輸出

execute request without error
after_request
teardown_request: None

訪問頁面 / 時,執行處理函數 index,執行請求結束后,會調用 after_request 和 teardown_request。該函數執行期間沒有發生異常,傳遞給 teardown_request 鉤子函數的 exception 對象為 None。

4. 在瀏覽器中訪問 http://localhost:5000/error

5. 觀察控制臺的輸出

execute request with error
Traceback (most recent call last):
  File "diff.py", line 14, in error
    1 / 0
ZeroDivisionError: division by zero
after_request
teardown_request: division by zero

訪問頁面 /error 時,執行處理函數 error,執行請求結束后,會調用 after_request 和 teardown_request。該函數執行期間發生異常,傳遞給 teardown_request 鉤子函數的 exception 對象為 ZeroDivisionError。

5. 錯誤處理

5.1 程序代碼 error.py

通過一個具體的例子講解使用 errorhandler 處理 HTTP 錯誤,代碼如下:

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    print('execute request')
    return 'Hello World'

@app.errorhandler(404)
def errorhandler(e):
    print('error_handler(404)')
    print(e)
    return '404 Error'

if __name__ == '__main__':
    app.run()

在第 10 行,使用 @errorhandler(404) 注冊一個鉤子函數 errorhandler(e),當發生 HTTP 404 錯誤(頁面不存在)時,調用鉤子函數,該函數的第一個參數是錯誤對象。

5.2 運行程序 error.py

1. 啟動程序 error.py

$ python error.py

2. 在瀏覽器中訪問 http://localhost:5000/non-exist-page

3. 觀察瀏覽器的顯示

404 Error

訪問一個不存在的頁面 /non-exist-page,產生 HTTP 404 錯誤,Flask 框架執行使用 @errorhandler(404) 注冊的函數 errorhandler,該函數返回字符串 ‘404 Error’ 給瀏覽器作為響應。

5. 觀察控制臺的輸出

error_handler(404)
404 Not Found: The requested URL was not found on the server. If
 URL manually please check your spelling and try again.

執行 errorhandler 時,首先打印字符串 ‘error_handler(404)’,然后打印 Error 對象。

6. 源代碼下載

7. 小結

本小節講解了鉤子函數的語法和常見的鉤子函數,對本小節講解的重點,使用思維導圖概括如下:

圖片描述