1. 程式人生 > >Flask框架鉤子函式使用方式及應用場景分析

Flask框架鉤子函式使用方式及應用場景分析

Flask框架鉤子函式使用方式及應用場景分析


在正常執行的程式碼前中後,強行插入執行一段你想要實現的功能的程式碼,這種函式就叫做鉤子函式。鉤子函式就是等同於高速公路上的收費站,進高速之前給你一個卡,並檢查你是否超重。離開之前收你,也可以攔住你安檢一下。

一. 基礎概念:

  • request: Flask的請求上下文,包含請求變數如:method、args、form、values、endpoint、headers、remote_addr都是比較常用的。

  • session:Flask的請求上下文,用於存放使用者的會話資訊。

  • current_app:Flask的應用上下文,返回當前app的方法和屬性,可以勉強理解為類全域性變數。

二. 七種鉤子

第一個鉤子:@app.before_first_request

只在第一次請求之前執行,也就是啟動專案,不會執行,只會在第一次有人發起請求時,才會觸發這個鉤子中的程式碼。

全域性場景:可以帶動一個非同步執行的函式,進行一些健康指標的檢查,如果發現有異常,則截斷後續的請求,將整個Flask應用停止。

@app.before_first_request
def first_request():
    print('只有在處理第一次請求之前執行')
第二個鉤子:@app.before_request

這是最重要的一個鉤子,在每次請求之前可以注入你要的邏輯的鉤子。在app下的before_request,過濾的是全部請求。結合Blueprint的before_request,則是過濾該藍圖下的請求。所以我們就可以進行分層過濾,定製化過濾。

全域性的場景包含:共享session的鑑權函式、請求黑白名單過濾、根據endpoint進行請求j等。

藍圖場景包含api的請求必填欄位校驗,是否json請求校驗,請求的token校驗等。

api = Blueprint('api', __name__)
requied = {
    'api.register':['email','username','password']
}

# 鉤子 在請求執行之前
@api.before_request
def before_request():

    # 請求格式校驗攔截
    if not request.is_json:
        return
'帶引數請求請使用json格式' # 缺少必填引數攔截 try: if request.endpoint in requied: if request.method == "POST": missparam_list = [x for x in requied[request.endpoint] if x.encode('utf8') not in list(parse.parse_qs(request.data).keys())] else: missparam_list = [x for x in requied[request.endpoint] if x not in request.json.keys()] if len(missparam_list) > 0: return "缺少以下引數:{0}" except Exception as e: app.logger.error(e) return "{0}".format(e)
第三個鉤子:@app.errorhandler

當訪問應用出錯時,根據錯誤響應碼,進行一些定製化的操作,如返回一個可愛的404頁面。也可以進行一些報錯登記。

場景:可以用redis進行錯誤請求計數,超過一定量則進行告警。可以重定向到一個定製的錯誤程式碼頁面等。

@app.errorhandler(404)
def page_not_found(error):
    return render_template('otherpage/404.html'),404
第四個鉤子:@app.context_processor

這個鉤子也很實用,是將一些常量按字典的格式返回,則可以在jinja2的模版中引用。這樣就不用在每個檢視函式中都render_template中重複去寫一次。程式碼更簡潔。

場景:在html中,直接用{{jidan}}就會在頁面顯示yao。等同於app.add_template_global(‘yao’,’'jidan)

@app.context_processor
def context_rocessor():
    return {'jidan':'yao'}

第五個鉤子:@app.after_request

和上個鉤子類似,差別在於是請求完成時執行,它和之前鉤子有點不同,必須傳入一個引數來接收響應物件,並在最後return 這個引數,也就是返回響應內容。

場景:一般用於格式化響應結果,包括響應請求頭,響應的格式等。

@app.after_request
def after_request(response):
    response.headers['jidan'] = 'yaoyao'
    return response
第六個鉤子:@app.teardown_request

和第五個鉤子功能類似,在響應銷燬時,執行一個繫結的函式。做一些操作。

區別點在於:

after_request: 每一個請求之後繫結一個函式,如果請求沒有異常。

teardown_request: 每一個請求之後繫結一個函式,即使遇到了異常。

場景:銷燬DB連線等。

@app.teardown_request
def teardown_db(exception):
    db = getattr(g, 'database', None)
    if db is not None:
        db.close()
第七個鉤子:@app.teardown_appcontext

之前介紹的大部分是請求上下文的鉤子,這個屬於應用上下文的鉤子。不管是否有異常,當APP上下文被移除之後執行的函式, 可以進行資料庫的提交或者回滾。

場景:DB事務操作。

@app.teardown_appcontext
def teardown(cmd=None):
    if cmd is None:
        db.session.commit()
    else:
        db.session.rollback()
    db.session.remove()

總結:

請求勾子

  • 請求勾子: 用於對請求事件監聽, 當發生了指定的請求事件時, 可以讓開發者進行一些自定義處理
@app.before_first_request
def initial():
    print("當伺服器被第一次請求前呼叫, 用於完成初始化工作, 比如建立資料庫連線")


@app.before_request
def prepare():
    print('每次請求前呼叫, 用於完成請求準備工作, 比如引數校驗, 資料統計, 過濾黑名單')


@app.after_request
def process(response):  # 監聽了這個處理, 就需要設定形參來接收響應物件
    print("每次請求後呼叫(響應發給前端之前), 用於對響應進行加工處理, 比如設定統一的響應頭資訊, 包裝資料")

    return response  # 加工後必須將響應返回


@app.teardown_request
def error_handle(error):  # 監聽了這個處理, 就需要設定形成那來接收異常資訊, 如果沒有異常 error = None
    print("每次請求後呼叫, 無論是否出現異常, 一般完成一些善後工作, 比如異常統計")