Flask入門很輕鬆 (二)
請求鉤子
在客戶端和伺服器互動的過程中,有些準備工作或掃尾工作需要處理,比如:
- 在請求開始時,建立資料庫連線;
- 在請求開始時,根據需求進行許可權校驗;
- 在請求結束時,指定資料的互動格式;
為了讓每個檢視函式避免編寫重複功能的程式碼,Flask提供了通用設定的功能,即請求鉤子。
請求鉤子是通過裝飾器的形式實現,Flask支援如下四種請求鉤子:
- before_first_request
- 在處理第一個請求前執行
- before_request
- 在每次請求前執行
- 如果在某修飾的函式中返回了一個響應,檢視函式將不再被呼叫
- after_request
- 如果沒有丟擲錯誤,在每次請求後執行
- 接受一個引數:檢視函式作出的響應
- 在此函式中可以對響應值在返回之前做最後一步修改處理
- 需要將引數中的響應在此引數中進行返回
- teardown_request:
- 在每次請求後執行
- 接受一個引數:錯誤資訊,如果有相關錯誤丟擲
- 需要設定flask的配置DEBUG=False,teardown_request才會接受到異常物件。
程式碼
config.py
class Config(object):
DEBUG = True
SECRET_KEY = "abcccddgadsag"
hook.py
from flask import Flask from config import Config app = Flask(__name__) app.config.from_object(Config) @app.before_first_request def before_firest_request(): print("----- before_first_requets-----") print("系統初始化的時候,執行這個鉤子方法") print("會在接收到第一個使用者請求時,執行這裡的程式碼") @app.before_request def before_request(): print("----before request") print("每一次接收到使用者請求時,執行這個鉤子方法") print("一般可以用來判斷許可權,或者轉換路由引數或者預處理客戶端的請求的資料") @app.after_request def after_request(response): print("----after_request----") print("在處理請求以後,執行這個鉤子方法") print("一般可以用於記錄會員/管理員的操作歷史,瀏覽歷史,清理收尾的工作") response.headers["Content-Type"] = "application/json" return response @app.teardown_request def teardown_request(exc): print("----teardown_request----") print("在每一次請求以後,執行這個鉤子方法,如果有異常錯誤,則會傳遞錯誤異常物件到當前方法的引數中") print(exc) @app.route("/hook") def hook(): print("----這是檢視函式----") print("檢視函式被運行了") return "這是檢視函式" if __name__ == '__main__': app.run(host="127.0.0.1", port=80)
- 請求時的列印:
----- before_first_requets----- 系統初始化的時候,執行這個鉤子方法 會在接收到第一個使用者請求時,執行這裡的程式碼 ----before request 每一次接收到使用者請求時,執行這個鉤子方法 一般可以用來判斷許可權,或者轉換路由引數或者預處理客戶端的請求的資料 ----這是檢視函式---- 檢視函式被運行了 ----after_request---- 在處理請求以後,執行這個鉤子方法 一般可以用於記錄會員/管理員的操作歷史,瀏覽歷史,清理收尾的工作 ----teardown_request---- 在每一次請求以後,執行這個鉤子方法,如果有異常錯誤,則會傳遞錯誤異常物件到當前方法的引數中 None
異常捕獲
主動丟擲HTTP異常
- abort 方法
- 丟擲一個給定狀態程式碼的 HTTPException 或者 指定響應,例如想要用一個頁面未找到異常來終止請求,你可以呼叫 abort(404)。
- 引數:
- code – HTTP的錯誤狀態碼
# abort(404)
abort(500)
丟擲狀態碼的話,只能丟擲 HTTP 協議的錯誤狀態碼
捕獲錯誤
- errorhandler 裝飾器
- 註冊一個錯誤處理程式,當程式丟擲指定錯誤狀態碼的時候,就會呼叫該裝飾器所裝飾的方法
- 引數:
- code_or_exception – HTTP的錯誤狀態碼或指定異常
- 例如統一處理狀態碼為500的錯誤給使用者友好的提示:
@app.errorhandler(500)
def internal_server_error(e):
return '伺服器搬家了'
- 捕獲指定異常型別
@app.errorhandler(ZeroDivisionError)
def zero_division_error(e):
return '除數不能為0'
上下文
上下文:即語境,語意,在程式中可以理解為在程式碼執行到某一時刻時,根據之前程式碼所做的操作以及下文即將要執行的邏輯,可以決定在當前時刻下可以使用到的變數,或者可以完成的事情。
Flask中有兩種上下文,請求上下文(request context)和應用上下文(application context)。
Flask中上下文物件:相當於一個容器,儲存了 Flask 程式執行過程中的一些資訊。
- application 指的就是當你呼叫
app = Flask(__name__)
建立的這個物件app
; - request 指的是每次
http
請求發生時,WSGI server
(比如gunicorn)呼叫Flask.__call__()
之後,在Flask
物件內部建立的Request
物件; - application 表示用於響應WSGI請求的應用本身,request 表示每次http請求;
- application的生命週期大於request,一個application存活期間,可能發生多次http請求,所以,也就會有多個request
請求上下文(request context)
思考:在檢視函式中,如何取到當前請求的相關資料?比如:請求地址,請求方式,cookie等等
在 flask 中,可以直接在檢視函式中使用 request 這個物件進行獲取相關資料,而 request 就是請求上下文的物件,儲存了當前本次請求的相關資料,請求上下文物件有:request、session
- request
- 封裝了HTTP請求的內容,針對的是http請求。舉例:user = request.args.get('user'),獲取的是get請求的引數。
- session
- 用來記錄請求會話中的資訊,針對的是使用者資訊。舉例:session['name'] = user.id,可以記錄使用者資訊。還可以通過session.get('name')獲取使用者資訊。
應用上下文(application context)
它的字面意思是 應用上下文,但它不是一直存在的,它只是request context 中的一個對 app 的代理(人),所謂local proxy。它的作用主要是幫助 request 獲取當前的應用,它是伴 request 而生,隨 request 而滅的。
應用上下文物件有:current_app,g
current_app
應用程式上下文,用於儲存應用程式中的變數,可以通過current_app.name列印當前app的名稱,也可以在current_app中儲存一些變數,例如:
- 應用的啟動指令碼是哪個檔案,啟動時指定了哪些引數
- 載入了哪些配置檔案,匯入了哪些配置
- 連線了哪個資料庫
- 有哪些可以呼叫的工具類、常量
- 當前flask應用在哪個機器上,哪個IP上執行,記憶體多大
current_app.name
current_app.test_value='value'
g變數
g 作為 flask 程式全域性的一個臨時變數,充當者中間媒介的作用,我們可以通過它傳遞一些資料,g 儲存的是當前請求的全域性變數,不同的請求會有不同的全域性變數,通過不同的thread id區別
g.name='abc'
注意:不同的請求,會有不同的全域性變數
兩者區別:
- 請求上下文:儲存了客戶端和伺服器互動的資料
- 應用上下文:flask 應用程式執行過程中,儲存的一些配置資訊,比如程式名、資料庫連線、應用資訊等
from flask import Flask
# 新增一個配置檔案,在配置檔案中設定配置資訊
from config import Config
from flask import request
app = Flask(__name__)
app.config.from_object(Config)
"""請求上下文"""
class Model(object):
def __init__(self):
print("模型接受到資料,num=%s" % request.args.get("username") )
@app.route("/context")
def context():
Model()
return "ok"
@app.route("/context2")
def context2():
Model()
return "ok"
"""應用上下文"""
from flask import current_app
@app.route('/context3')
def context3():
# current_app 只是app物件在檢視被請求時的一個代理物件[別名物件]
print( current_app.username ) # 我們可以直接呼叫app物件所擁有的屬性和方法
return "應用上下文"
from flask import g
class Model2(object):
def __init__(self):
print("模型接受到資料,num=%s" % g.username )
@app.route('/context4')
def context4():
# g是一個臨時的全域性物件,只會在本次請求中獲取到資料
g.username = request.args.get("username")
Model2()
return "應用上下文"
if __name__ == '__main__':
# app 系統應用物件
app.username='應用上下文的username'
print('----執行專案之前----')
app.run()
Flask-Script 擴充套件
安裝命令:
pip install flask-script
整合 Flask-Script到flask應用中
from flask import Flask
app = Flask(__name__)
"""使用flask_script啟動專案"""
from flask_script import Manager
manage = Manager(app)
@app.route('/')
def index():
return 'hello world'
if __name__ == "__main__":
manager.run()
Flask-Script 還可以為當前應用程式新增指令碼命令
"""自定義flask_script終端命令"""
from flask_script import Command
class HelloCommand(Command):
"""命令的相關描述"""
def run(self):
with open("text.txt","w") as f:
f.write("hello\r\nhello")
pass
print("這是執行了hello命令")
manage.add_command('hello', HelloCommand() )