flask 核心 之 應用上下文 及 請求上下文
阿新 • • 發佈:2018-11-14
Werkzeugs 是 Flask 的底層WSGI庫。
什麼是WSGI?
一段簡單的app:
def dispath_request(self, request): return Response('Hello World!') def wsgi_app(self, environ, start_response): request = Request(environ) response = self.dispath_request(request) return response(environ, start_response)
environ: 包含全部的HTTP請求資訊的字典,由WSGI Server
解包 HTTP 請求生成。
**start_response:**一個WSGI Server
提供的函式,呼叫可以返回相應的狀態嗎和HTTP報文頭,函式在返回前必須呼叫一次。
Flask的上下文物件
Flask有兩種Context(上下文),分別是
- RequestContext 請求上下文;
- Request 請求的物件,封裝了Http請求(environ)的內容,生命週期請求處理完就結束了;
- Session 根據請求中的cookie,重新載入該訪問者相關的會話資訊;
- AppContext 應用上下文;
- g 處理請求時用作臨時儲存的物件。每次請求都會重設這個變數, 生命週期請求處理完就結束了;
- current_app 當前啟用程式的程式例項,只要當前程式還在執行就不會失效。
flask 處理請求和響應的流程:
在 ‘flask/globals.py’ 程式碼中:
# context locals
_request_ctx_stack = LocalStack()
# LocalStack 是由werkzeug提供的棧結構類提供了push、pop等方法
# 並且Local物件是werkzeug開發的類似 thinking.local(用於隔離不同執行緒間的全域性變數) 物件,實現了在同一個協程中資料的隔離和全域性性,具體怎麼實現看原始碼,暫時沒看明白
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
# partial(_lookup_req_object, 'request') 總是返回 LocalStack 棧頂物件的 request 屬性
# LocalProxy 用於代理Local物件和LocalStack物件,至於為什麼使用代理。。
request = LocalProxy(partial(_lookup_req_object, 'request'))
session = LocalProxy(partial(_lookup_req_object, 'session'))
g = LocalProxy(partial(_lookup_app_object, 'g'))
flask local https://www.jianshu.com/p/3f38b777a621
為什麼要使用 LocalProxy 而不直接使用 Local 和 LocalStack
# AppContext 應用上下文
#
# 在flask/app.py下:
#
def app_context(self): # self 就是app物件
return AppContext(self)
#
# 在 `flask/ctx.py` 程式碼中
#
class AppContext(object):
def __init__(self, app):
self.app = app
self.url_adapter = app.create_url_adapter(None)
self.g = app.app_ctx_globals_class()
# Like request context, app contexts can be pushed multiple times
# but there a basic "refcount" is enough to track them.
self._refcnt = 0
def push(self): # 這個方法就是把應用上下文push到LocalStack,AppContext類有__enter__方法
"""Binds the app context to the current context."""
self._refcnt += 1
if hasattr(sys, 'exc_clear'):
sys.exc_clear()
_app_ctx_stack.push(self)
appcontext_pushed.send(self.app)
#
# 在 flask/cli.py 中有
#
def with_appcontext(f):
@click.pass_context
def decorator(__ctx, *args, **kwargs): # decorator 被裝飾器後 _ctx 引數是 threading 的 local() 物件
with __ctx.ensure_object(ScriptInfo).load_app().app_context(): # 在這裡就把 應用上下文push到了LocalStack
return __ctx.invoke(f, *args, **kwargs)
return update_wrapper(decorator, f)
# RequestContext 請求上下文
#
#在flask/app.py下
#
def request_context(self, environ): # 一次請求的環境變數
return RequestContext(self, environ)
#
# 在flask/ctx.py下:
#
class RequestContext(object):
def __init__(self, app, environ, request=None):
self.app = app
if request is None:
request = app.request_class(environ)
self.request = request
self.url_adapter = app.create_url_adapter(self.request)
self.flashes = None
self.session = None
...
...
def push(self):
...
_request_ctx_stack.push(self)
#
#在flask/app.py下
#
def wsgi_app(self, environ, start_response): # 這裡類似上面的那小段簡單 Werkzeugs app
ctx = self.request_context(environ)
error = None
try:
try:
ctx.push()
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except:
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
到此從 AppContext 到 RequestContext 梳理完
參考資料:
Flask的核心機制!關於請求處理流程和上下文
Werkzeug(Flask)之Local、LocalStack和LocalProxy