flask0.1版本源碼淺析——請求上下文
阿新 • • 發佈:2017-10-10
函數 light host con contex 準備 patch remove quest
說先flask應用在請求時會調用 wsgi_app(self, environ, start_response) 這個方法
def wsgi_app(self, environ, start_response): # ... with self.request_context(environ): rv = self.preprocess_request() if rv is None: rv = self.dispatch_request() response= self.make_response(rv) response = self.process_response(response) return response(environ, start_response)
這個函數的基本步驟是:
- 先是 打開request_context(environ), 然後我們就有了我們需要的“全局request”
- 然後preprocess_request()是處理數據前的準備
- 接下來調用
dispatch_request()
方法,這個方法會根據URL匹配的情況調用相關的視圖函數 - 之後調用
response(environ, start_response)
WSGI
服務器 - 最後with語句結束,當前線程會被銷毀
request_context
可以看出,requests_context 方法會調用 _RequestContext(self, environ) 類, 所以我們看這個類就可以
def request_context(self, environ): """Creates a request context from the given environment and binds it to the current context. This must be used in combination with the `with` statement because the request is only bound to the current context for the duration of the `with` block. :params environ: a WSGI environment""" return _RequestContext(self, environ)
_RequestContext(self, environ)
class _RequestContext(object): """The request context contains all request relevant information. It is created at the beginning of the request and pushed to the `_request_ctx_stack` and removed at the end of it. It will create the URL adapter and request object for the WSGI environment provided. """ def __init__(self, app, environ): self.app = app self.url_adapter = app.url_map.bind_to_environ(environ) self.request = app.request_class(environ) self.session = app.open_session(self.request) self.g = _RequestGlobals() self.flashes = None def __enter__(self): _request_ctx_stack.push(self) def __exit__(self, exc_type, exc_value, tb): # do not pop the request stack if we are in debug mode and an # exception happened. This will allow the debugger to still # access the request object in the interactive shell. if tb is None or not self.app.debug: _request_ctx_stack.pop()
其中對該類的屬性:
- app —— 屬於當前Flask應用
- url_adapter —— 創建一個 Map 實例,為了註冊路由使用
- request —— 接收 environ 後創建了 Request 的實例,儲存請求信息
- session —— 接收 request 信息後,創建Session的相關對象,儲存會話信息
- g —— 存儲全局變量
- flash —— 消息閃現
_request_ctx_stack
_request_ctx_stack = LocalStack()
_request_ctx_stack 是由 LocalStack 類實現的類似棧的結構。
我們通過 push(self) 可以看出,_request_ctx_stack 將 request_context 存儲到裏面。存儲結構為
{14272: {‘stack‘: [<RequestContext ‘http://localhost/‘ [GET] of test5>]}, 16696: {‘stack‘: [<RequestContext ‘http://localhost/‘ [GET] of test5>]}, 16856: {‘stack‘: [<RequestContext ‘http://localhost/‘ [GET] of test5>]}}
測試的代碼為
from flask import Flask, _request_ctx_stack import threading app = Flask(__name__) print _request_ctx_stack._local.__storage__ def foo(): request_context = app.test_request_context() _request_ctx_stack.push(request_context) q = [] for i in range(3): t = threading.Thread(target=foo) t.start() q.append(t) for j in q: j.join()View Code
這是一個字典形式的結構,鍵代表當前線程的數值,值代表當前線程存儲的變量,這樣很容易實現線程分離,從而使每個線程都可訪問自己的內的“全局變量”
current_app = LocalProxy(lambda: _request_ctx_stack.top.app) request = LocalProxy(lambda: _request_ctx_stack.top.request) session = LocalProxy(lambda: _request_ctx_stack.top.session) g = LocalProxy(lambda: _request_ctx_stack.top.g)
LocalProxy
是一個 Local
對象的代理,負責把所有對自己的操作轉發給內部的 Local
對象。
我們可以看出,current_app,request,session,g 都會是自己線程內的“全局變量”
with
這裏的 __enter__ 和 __exit__ 方法是為了 wsgi_app 內 with 語句調用
flask0.1版本源碼淺析——請求上下文