### 導航
- [索引](# "總目錄")
- [下一頁](# "用藍圖實現模塊化的應用") |
- [上一頁](# "應用上下文") |
- [Flask 0.10.1 文檔](#) ?
# 請求上下文
這部分文檔描述了在 Flask 0.7 中的行為,與舊的行為基本一致,但有細小微妙的差異。
這里推薦先閱讀 [*應用上下文*](#) 章節。
### 深入上下文作用域
比如說你有一個應用函數返回用戶應該跳轉到的 URL 。想象它總是會跳轉到 URL的 next 參數,或 HTTP referrer ,或索引頁:
~~~
from flask import request, url_for
def redirect_url():
return request.args.get('next') or \
request.referrer or \
url_for('index')
~~~
如你所見,它訪問了請求對象。當你試圖在純 Python shell 中運行這段代碼時,你會看見這樣的異常:
~~~
>>> redirect_url()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'request'
~~~
這有很大意義,因為我們當前并沒有可以訪問的請求。所以我們需要制造一個請求并且綁定到當前的上下文。 [test_request_context](# "flask.Flask.test_request_context") 方法為我們創建一個 [RequestContext](# "flask.ctx.RequestContext"):
~~~
>>> ctx = app.test_request_context('/?next=http://example.com/')
~~~
可以通過兩種方式利用這個上下文:使用 with 聲明或是調用[push()](# "flask.ctx.RequestContext.push") 和[pop()](# "flask.ctx.RequestContext.pop") 方法:
~~~
>>> ctx.push()
~~~
從這點開始,你可以使用請求對象:
~~~
>>> redirect_url()
u'http://example.com/'
~~~
直到你調用 pop:
~~~
>>> ctx.pop()
~~~
因為請求上下文在內部作為一個棧來維護,所以你可以多次壓棧出棧。這在實現內部重定向之類的東西時很方便。
更多如何從交互式 Python shell 中利用請求上下文的信息,請見 [*與 Shell 共舞*](#)章節。
### 上下文如何工作
如果你研究 Flask WSGI 應用內部如何工作,你會找到和這非常相似的一段代碼:
~~~
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](# "flask.ctx.RequestContext") 對象,并結合 with 聲明來綁定上下文。從相同線程中被調用的一切,直到 with 聲明結束前,都可以訪問全局的請求變量( [flask.request](# "flask.request") 和其它)。
請求上下文內部工作如同一個棧。棧頂是當前活動的請求。[push()](# "flask.ctx.RequestContext.push") 把上下文添加到棧頂,[pop()](# "flask.ctx.RequestContext.pop") 把它移出棧。在出棧時,應用的[teardown_request()](# "flask.Flask.teardown_request") 函數也會被執行。
另一件需要注意的事是,請求上下文被壓入棧時,并且沒有當前應用的應用上下文,它會自動創建一個 [*應用上下文*](#) 。
### 回調和錯誤
在 Flask 中,請求處理時發生一個錯誤時會發生什么?這個特殊的行為在 0.7 中變更了,因為我們想要更簡單地得知實際發生了什么。新的行為相當簡單:
1. 在每個請求之前,執行 [before_request()](# "flask.Flask.before_request") 上綁定的函數。如果這些函數中的某個返回了一個響應,其它的函數將不再被調用。任何情況下,無論如何這個返回值都會替換視圖的返回值。
1. 如果 [before_request()](# "flask.Flask.before_request") 上綁定的函數沒有返回一個響應,常規的請求處理將會生效,匹配的視圖函數有機會返回一個響應。
1. 視圖的返回值之后會被轉換成一個實際的響應對象,并交給[after_request()](# "flask.Flask.after_request") 上綁定的函數適當地替換或修改它。
1. 在請求的最后,會執行 [teardown_request()](# "flask.Flask.teardown_request") 上綁定的函數。這總會發生,即使在一個未處理的異常拋出后或是沒有請求前處理器執行過(例如在測試環境中你有時會想不執行請求前回調)。
現在錯誤時會發生什么?在生產模式中,如果一個異常沒有被捕獲,將調用500 internal server 的處理。在生產模式中,即便異常沒有被處理過,也會往上冒泡拋給給 WSGI 服務器。如此,像交互式調試器這樣的東西可以提供有用的調試信息。
在 0.7 中做出的一個重大變更是內部服務器錯誤不再被請求后回調傳遞處理,而且請求后回調也不再保證會執行。這使得內部的調度代碼更簡潔,易于定制和理解。
新的綁定于銷毀請求的函數被認為是用于代替那些請求的最后絕對需要發生的事。
### 銷毀回調
銷毀回調是是特殊的回調,因為它們在不同的點上執行。嚴格地說,它們不依賴實際的請求處理,因為它們限定在 [RequestContext](# "flask.ctx.RequestContext") 對象的生命周期。當請求上下文出棧時, [teardown_request()](# "flask.Flask.teardown_request") 上綁定的函數會被調用。
這對于了解請求上下文的壽命是否因為在 with 聲明中使用測試客戶端或在命令行中使用請求上下文時被延長很重要:
~~~
with app.test_client() as client:
resp = client.get('/foo')
# the teardown functions are still not called at that point
# even though the response ended and you have the response
# object in your hand
# only when the code reaches this point the teardown functions
# are called. Alternatively the same thing happens if another
# request was triggered from the test client
~~~
從這些命令行操作中,很容易看出它的行為:
~~~
>>> 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
>>>
~~~
注意銷毀回調總是會被執行,即使沒有請求前回調執行過,或是異常發生。測試系統的特定部分也會臨時地在不調用請求前處理器的情況下創建請求上下文。確保你寫的請求銷毀處理器不會報錯。
### 留意代理
Flask 中提供的一些對象是其它對象的代理。背后的原因是,這些代理在線程間共享,并且它們在必要的情景中被調度到限定在一個線程中的實際的對象。
大多數時間你不需要關心它,但是在一些例外情況中,知道一個對象實際上是代理是有益的:
- 代理對象不會偽造它們繼承的類型,所以如果你想運行真正的實例檢查,你需要在被代理的實例上這么做(見下面的 _get_current_object )。
- 如果對象引用是重要的(例如發送 [*信號*](#) )
如果你需要訪問潛在的被代理的對象,你可以使用[_get_current_object()](http://werkzeug.pocoo.org/docs/local/#werkzeug.local.LocalProxy._get_current_object "(在 Werkzeug v0.10)") [http://werkzeug.pocoo.org/docs/local/#werkzeug.local.LocalProxy._get_current_object] 方法:
~~~
app = current_app._get_current_object()
my_signal.send(app)
~~~
### 錯誤時的上下文保護
無論錯誤出現與否,在請求的最后,請求上下文會出棧,并且相關的所有數據會被銷毀。在開發中,當你想在異常發生時,長期地獲取周圍的信息,這會成為麻煩。在 Flask 0.6 和更早版本中的調試模式,如果發生異常,請求上下文不會被彈出棧,這樣交互式調試器才能提供給你重要信息。
從 Flask 0.7 開始,我們設定 PRESERVE_CONTEXT_ON_EXCEPTION 配置變量來更好地控制該行為。這個值默認與 DEBUG 的設置相關。當應用工作在調試模式下時,上下文會被保護,而生產模式下相反。
不要在生產模式強制激活 PRESERVE_CONTEXT_ON_EXCEPTION ,因為它會導致在異常時應用的內存泄露。不過,它在開發時獲取開發模式下相同的錯誤行為來試圖調試一個只有生產設置下才發生的錯誤時很有用。
? 版權所有 2013, Armin Ronacher.
- 歡迎使用 Flask
- 前言
- 給有經驗程序員的前言
- 安裝
- 快速入門
- 教程
- 介紹 Flaskr
- 步驟 0: 創建文件夾
- 步驟 1: 數據庫模式
- 步驟 2: 應用設置代碼
- 步驟 3: 創建數據庫
- 步驟 4: 請求數據庫連接
- 步驟 5: 視圖函數
- 步驟 6: 模板
- 步驟 7: 添加樣式
- 福利: 應用測試
- 模板
- 測試 Flask 應用
- 記錄應用錯誤
- 配置處理
- 信號
- 即插視圖
- 應用上下文
- 請求上下文
- 用藍圖實現模塊化的應用
- Flask 擴展
- 與 Shell 共舞
- Flask 代碼模式
- 大型應用
- 應用程序的工廠函數
- 應用調度
- 使用 URL 處理器
- 部署和分發
- 使用 Fabric 部署
- 在 Flask 中使用 SQLite 3
- 在 Flask 中使用 SQLAlchemy
- 上傳文件
- 緩存
- 視圖裝飾器
- 使用 WTForms 進行表單驗證
- 模板繼承
- 消息閃現
- 用 jQuery 實現 Ajax
- 自定義錯誤頁面
- 延遲加載視圖
- 在 Flask 中使用 MongoKit
- 添加 Favicon
- 數據流
- 延遲請求回調
- 添加 HTTP Method Overrides
- 請求內容校驗碼
- 基于 Celery 的后臺任務
- 部署選擇
- mod_wsgi (Apache)
- 獨立 WSGI 容器
- uWSGI
- FastCGI
- CGI
- 聚沙成塔
- API
- JSON 支持
- Flask 中的設計決策
- HTML/XHTML 常見問題
- 安全注意事項
- Flask 中的 Unicode
- Flask 擴展開發
- Pocoo 風格指引
- Python 3 支持
- 升級到最新版本
- Flask Changelog
- 許可證
- 術語表