轉載請在文章開頭附上原文鏈接地址:https://www.cnblogs.com/Sunzz/p/10959454.html
請求鈎子
在客戶端和服務器交互的過程中,有些准備工作或掃尾工作需要處理,比如:
- 在請求開始時,建立數據庫連接;
- 在請求開始時,根據需求進行權限校驗;
- 在請求結束時,指定數據的交互格式;
為了讓每個視圖函數避免編寫重復功能的代碼,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() )