1. 程式人生 > >Flask入門很輕鬆 (二)

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 程式執行過程中的一些資訊。

  1. application 指的就是當你呼叫app = Flask(__name__)建立的這個物件app
  2. request 指的是每次http請求發生時,WSGI server(比如gunicorn)呼叫Flask.__call__()之後,在Flask物件內部建立的Request物件;
  3. application 表示用於響應WSGI請求的應用本身,request 表示每次http請求;
  4. 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() )