本文講述 Flask 0.7 版本的運行方式,與舊版本的運行方式基本相同,但也有一些細(xì)微的 差別。
建議你在閱讀本文之前,先閱讀應(yīng)用環(huán)境 。
假設(shè)有一個工具函數(shù),這個函數(shù)返回用戶重定向的 URL (包括 URL 的 next 參數(shù)、 或 HTTP 推薦和索引頁面):
from flask import request, url_for
def redirect_url():
return request.args.get('next') or \
request.referrer or \
url_for('index')
如上例所示,這個函數(shù)訪問了請求對象。如果你在一個普通的 Python 解釋器中運行這個函數(shù),那么會看到如下異常:
>>> redirect_url()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'request'
這是因為現(xiàn)在我們沒有一個可以訪問的請求。所以我們只能創(chuàng)建一個請求并綁定到當(dāng)前環(huán)境中。 test_request_context 方法可以創(chuàng)建一個 RequestContext :
>>> ctx = app.test_request_context('/?next=http://example.com/')
這個環(huán)境有兩種使用方法:一種是使用 with 語句;另一種是調(diào)用 push() 和 pop() 方法:
>>> ctx.push()
現(xiàn)在可以使用請求對象了:
>>> redirect_url()
u'http://example.com/'
直到你調(diào)用 pop :
>>> ctx.pop()
可以把請求環(huán)境理解為一個堆棧,可以多次壓入和彈出,可以方便地執(zhí)行一個像內(nèi)部重定向之類的東西。
關(guān)于在 Python 解釋器中使用請求環(huán)境的更多內(nèi)容參見在 Shell 中使用 Flask 。
如果深入 Flask WSGI 應(yīng)用內(nèi)部,那么會找到類似如下代碼:
def wsgi_app(self, environ):
with self.request_context(environ):
try:
response = self.full_dispatch_request()
except Exception, e:
response = self.make_response(self.handle_exception(e))
return response(environ, start_response)
request_context() 方法返回一個新的 RequestContext 對象,并且使用 with 語句把這個對象綁定到環(huán)境。在 with 語句塊中,在同一個線程中調(diào)用的所有東西可以訪問全局請求 (flask.request 或其他)。
請求環(huán)境的工作方式就像一個堆棧,棧頂是當(dāng)前活動請求。 push() 把環(huán)境壓入堆棧中,而 pop() 把環(huán)境彈出。彈出的同時,會執(zhí)行應(yīng)用的 teardown_request() 函數(shù)。
另一件要注意的事情是:請求環(huán)境會在壓入時自動創(chuàng)建一個應(yīng)用環(huán)境 。在此之前,應(yīng)用沒有應(yīng)用環(huán)境。
如果在請求處理的過程中發(fā)生錯誤,那么 Flask 會如何處理呢?自 Flask 0.7 版本之后, 處理方式有所改變。這是為了更方便地反映到底發(fā)生了什么情況。新的處理方式非常簡單:
在每個請求之前,會執(zhí)行所有 before_request() 函數(shù)。如果 其中一個函數(shù)返回一個響應(yīng),那么其他函數(shù)將不再調(diào)用。但是在任何情況下,這個 返回值將會替代視圖的返回值。
如果 before_request() 函數(shù)均沒有響應(yīng),那么就會進(jìn)行正常的 請求處理,匹配相應(yīng)的視圖,返回響應(yīng)。
接著,視圖的返回值會轉(zhuǎn)換為一個實際的響應(yīng)對象并交給 after_request() 函數(shù)處理。在處理過程中,這個對象可能會被 替換或修改。
那么如果出錯了會怎么樣?在生產(chǎn)模式下,如果一個異常未被主要捕獲處理,那么會調(diào)用 500 內(nèi)部服務(wù)器處理器。在開發(fā)模式下,引發(fā)的異常不再被進(jìn)一步處理,會提交給 WSGI 服務(wù)器。因此,需要使用交互調(diào)試器來查看調(diào)試信息。
Flask 0.7 版本的重大變化是內(nèi)部服務(wù)器錯誤不再由請求后回調(diào)函數(shù)來處理,并且請求后 回調(diào)函數(shù)也不保證一定被執(zhí)行。這樣使得內(nèi)部調(diào)試代碼更整潔、更易懂和更容易定制。
同時還引入了新的卸載函數(shù),這個函數(shù)在請求結(jié)束時一定會執(zhí)行。
卸載回調(diào)函數(shù)的特殊之處在于其調(diào)用的時機(jī)是不固定的。嚴(yán)格地說,調(diào)用時機(jī)取決于其綁定的 RequestContext 對象的生命周期。當(dāng)請求環(huán)境彈出時就 會調(diào)用 teardown_request() 函數(shù)。
請求環(huán)境的生命周期是會變化的,當(dāng)請求環(huán)境位于測試客戶端中的 with 語句中或者在 命令行下使用請求環(huán)境時,其生命周期會被延長。因此知道生命周期是否被延長是很重要的:
with app.test_client() as client:
resp = client.get('/foo')
# 到這里還沒有調(diào)用卸載函數(shù)。即使這時響應(yīng)已經(jīng)結(jié)束,并且已經(jīng)
# 獲得響應(yīng)對象,還是不會調(diào)用卸載函數(shù)。
# 只有到這里才會調(diào)用卸載函數(shù)。另外,如果另一個請求在客戶端中被
# 激發(fā),也會調(diào)用卸載函數(shù)。
在使用命令行時,可以清楚地看到運行方式:
>>> app = Flask(__name__)
>>> @app.teardown_request
... def teardown_request(exception=None):
... print 'this runs after request'
...
>>> ctx = app.test_request_context()
>>> ctx.push()
>>> ctx.pop()
this runs after request
>>>
記牢記:卸載函數(shù)在任何情況下都會被執(zhí)行,甚至是在請求預(yù)處理回調(diào)函數(shù)沒有執(zhí)行, 但是發(fā)生異常的情況下。有的測試系統(tǒng)可能會臨時創(chuàng)建一個請求環(huán)境,但是不執(zhí)行 預(yù)處理器。請正確使用卸載處理器,確保它們不會執(zhí)行失敗。
部分 Flask 提供的對象是其他對象的代理。使用代理的原因是代理對象共享于不同的 線程,它們在后臺根據(jù)需要把實際的對象分配給不同的線程。
多數(shù)情況下,你不需要關(guān)心這個。但是也有例外,在下列情況有下,知道對象是一個代理對象是有好處的:
想要執(zhí)行真正的實例檢查的情況。因為代理對象不會假冒被代理對象的對象類型, 因此,必須檢查被代理的實際對象(參見下面的 _get_current_object )。 對象引用非常重要的情況(例如發(fā)送 信號 )。 如果想要訪問被代理的對象,可以使用 _get_current_object() 方法:
app = current_app._get_current_object()
my_signal.send(app)
不管是否出錯,在請求結(jié)束時,請求環(huán)境會被彈出,并且所有相關(guān)聯(lián)的數(shù)據(jù)會被銷毀。 但是在開發(fā)過程中,可能需要在出現(xiàn)異常時保留相關(guān)信息。在 Flask 0.6 版本及更早的 版本中,在發(fā)生異常時,請求環(huán)境不會被彈出,以便于交互調(diào)試器提供重要信息。
自 Flask 0.7 版本開始,可以通過設(shè)置 PRESERVE_CONTEXT_ON_EXCEPTION
配置變量來更好地控制環(huán)境的保存。缺省情況下,這個配置變更與 DEBUG 變更關(guān)聯(lián)。如果在調(diào)試模式下,那么環(huán)境會被保留,而在生產(chǎn)模式下則不保留。
不要在生產(chǎn)環(huán)境下強(qiáng)制激活 PRESERVE_CONTEXT_ON_EXCEPTION
,因為這會在出現(xiàn)異常 時導(dǎo)致應(yīng)用內(nèi)存溢出。但是在調(diào)試模式下使用這個變更是十分有用的,你可以獲得在生產(chǎn)模式下出錯時的環(huán)境。
? Copyright 2013, Armin Ronacher. Created using Sphinx.