Werkzeugs 是 Flask 的底層WSGI庫。
一段簡單的app:html
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報文頭,函數在返回前必須調用一次。python
Flask有兩種Context(上下文),分別是flask
RequestContext 請求上下文;cookie
AppContext 應用上下文;session
在 'flask/globals.py' 代碼中:app
# 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)
before_request(self, f)
# 註冊一個在請求到達前要執行的函數before_first_request(self, f)
# 註冊在整個應用實例第一個請求到達前要執行的函數after_request(self, f)
# 註冊一個在請求處理後的函數teardown_request(self, f)
# 註冊在請求上下文棧彈出後要執行的函數teardown_appcontext(self, f)
# 註冊在應用上下文結束時要執行的函數context_processor(self, f)
# 向模板上下文中注入變量和方法,這個f
必須返回一個字典, 在渲染的模板中使用url_defaults(self, f)
# 爲應用程序中的全部視圖函數 註冊URL值預處理器函數。 這些函數將在:before_request
函數以前調用。url_value_preprocessor(self, f)
# 註冊一個 ‘在請求的url匹配後視圖函數執行前,把環境中的某些變量換個位置存儲的’ 函數# url_defaults(self, f) 和 url_value_preprocessor(self, f) 的使用 from flask import Blueprint, render_template profile = Blueprint('profile', __name__, url_prefix='/<user_url_slug>') @profile.url_defaults def add_user_url_slug(endpoint, values): values.setdefault('user_url_slug', g.user_url_slug) @profile.url_value_preprocessor def pull_user_url_slug(endpoint, values): g.user_url_slug = values.pop('user_url_slug') query = User.query.filter_by(url_slug=g.user_url_slug) g.profile_owner = query.first_or_404() @profile.route('/') def timeline(): return render_template('profile/timeline.html') @profile.route('/photos') def photos(): return render_template('profile/photos.html') @profile.route('/about') def about(): return render_template('profile/about.html')
dispatch_request(self)
: 匹配路由,返回視圖函數或者錯誤處理函數的返回值,而且檢測是否爲option
請求,若是是則構造默認的 ‘options response’ 響應。構造過程首先是 Request
uri 所支持的方法集(get、post、等),而後更新 Response
中 allow
屬性(set類型),最後返回Response
對象,若不是option
請求則執行視圖函數;make_response(self, rv)
: rv
是視圖函數的返回值,在python3中,rv能夠使一個元組(body、status、headers)、Response
類對象、 或者一個返回Response
類對象的回調函數。這個函數的功能就是把視圖函數返回的 status headers 綁定到 Response;create_url_adapter(self, request)
:url_map 適配器。對werkzeug的Map的bind
和bind_to_environ
兩個方法進行了封裝。bind
: 綁定一個主機地址,並返回MapAdapter對象 ; bind_to_environ
( 將MAP綁定到WSGI環境中,並返回MapAdapter對象(參數script_name在進行重定向時會用到);try_trigger_before_first_request_functions(self)
:在該實例第一個請求到達時把當前線程加鎖,而後依次執行被 before_first_request(self, f)
裝飾過得函數,而後釋放鎖並把_got_first_request
置爲True,再次就直接return;preprocess_request(self)
:該函數就是執行被url_value_preprocessor
和 before_request
裝飾過的函數;full_dispatch_request(self)
: 先執行try_trigger_before_first_request_functions
,而後執行preprocess_request
,若before_request
中的函數有返回值則爲其構造Response
,而後跳過排在此函數後邊的函數包括視圖函數,若before_request
的函數都返回的None或沒有函數就執行 dispatch_request(self)
;inject_url_defaults(self, error, endpoint, values)
: 執行被'url_defaults' 裝飾的函數process_response(self, response)
: 同preprocess_request(self)
click 包 把python代碼打包成命令行工具
mimetypes 包 查看文件類型
itsdangerous 簽名 序列化字符串工具
參考資料:
Flask的核心機制!關於請求處理流程和上下文
Werkzeug(Flask)之Local、LocalStack和LocalProxypost