摘要:的上下文對(duì)象有兩種上下文,分別是請(qǐng)求上下文請(qǐng)求的對(duì)象,封裝了請(qǐng)求的內(nèi)容,生命周期請(qǐng)求處理完就結(jié)束了根據(jù)請(qǐng)求中的,重新載入該訪問(wèn)者相關(guān)的會(huì)話信息應(yīng)用上下文處理請(qǐng)求時(shí)用作臨時(shí)存儲(chǔ)的對(duì)象。
Werkzeugs 是 Flask 的底層WSGI庫(kù)。什么是WSGI?
一段簡(jiǎn)單的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請(qǐng)求信息的字典,由WSGI Server解包 HTTP 請(qǐng)求生成。
start_response:一個(gè)WSGI Server 提供的函數(shù),調(diào)用可以返回相應(yīng)的狀態(tài)嗎和HTTP報(bào)文頭,函數(shù)在返回前必須調(diào)用一次。
Flask有兩種Context(上下文),分別是
RequestContext 請(qǐng)求上下文;
Request 請(qǐng)求的對(duì)象,封裝了Http請(qǐng)求(environ)的內(nèi)容,生命周期請(qǐng)求處理完就結(jié)束了;
Session 根據(jù)請(qǐng)求中的cookie,重新載入該訪問(wèn)者相關(guān)的會(huì)話信息;
AppContext 應(yīng)用上下文;
g 處理請(qǐng)求時(shí)用作臨時(shí)存儲(chǔ)的對(duì)象。每次請(qǐng)求都會(huì)重設(shè)這個(gè)變量, 生命周期請(qǐng)求處理完就結(jié)束了;
current_app 當(dāng)前激活程序的程序?qū)嵗?,只要?dāng)前程序還在運(yùn)行就不會(huì)失效。
flask 處理請(qǐng)求和響應(yīng)的流程:在 "flask/globals.py" 代碼中:
# context locals _request_ctx_stack = LocalStack() # LocalStack 是由werkzeug提供的棧結(jié)構(gòu)類提供了push、pop等方法 # 并且Local對(duì)象是werkzeug開(kāi)發(fā)的類似 thinking.local(用于隔離不同線程間的全局變量) 對(duì)象,實(shí)現(xiàn)了在同一個(gè)協(xié)程中數(shù)據(jù)的隔離和全局性,具體怎么實(shí)現(xiàn)看源代碼,暫時(shí)沒(méi)看明白 _app_ctx_stack = LocalStack() current_app = LocalProxy(_find_app) # partial(_lookup_req_object, "request") 總是返回 LocalStack 棧頂對(duì)象的 request 屬性 # LocalProxy 用于代理Local對(duì)象和LocalStack對(duì)象,至于為什么使用代理。。 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 應(yīng)用上下文 # # 在flask/app.py下: # def app_context(self): # self 就是app對(duì)象 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): # 這個(gè)方法就是把應(yīng)用上下文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 參數(shù)是 threading 的 local() 對(duì)象 with __ctx.ensure_object(ScriptInfo).load_app().app_context(): # 在這里就把 應(yīng)用上下文push到了LocalStack return __ctx.invoke(f, *args, **kwargs) return update_wrapper(decorator, f) # RequestContext 請(qǐng)求上下文 # #在flask/app.py下 # def request_context(self, environ): # 一次請(qǐng)求的環(huán)境變量 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): # 這里類似上面的那小段簡(jiǎn)單 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 梳理完 app.py 源碼中有這么幾個(gè)裝飾器:
before_request(self, f) # 注冊(cè)一個(gè)在請(qǐng)求到達(dá)前要執(zhí)行的函數(shù)
before_first_request(self, f) # 注冊(cè)在整個(gè)應(yīng)用實(shí)例第一個(gè)請(qǐng)求到達(dá)前要執(zhí)行的函數(shù)
after_request(self, f) # 注冊(cè)一個(gè)在請(qǐng)求處理后的函數(shù)
teardown_request(self, f) # 注冊(cè)在請(qǐng)求上下文棧彈出后要執(zhí)行的函數(shù)
teardown_appcontext(self, f) # 注冊(cè)在應(yīng)用上下文結(jié)束時(shí)要執(zhí)行的函數(shù)
context_processor(self, f) # 向模板上下文中注入變量和方法,這個(gè)f必須返回一個(gè)字典, 在渲染的模板中使用
url_defaults(self, f) # 為應(yīng)用程序中的所有視圖函數(shù) 注冊(cè)URL值預(yù)處理器函數(shù)。 這些函數(shù)將在:before_request函數(shù)之前調(diào)用。
url_value_preprocessor(self, f) # 注冊(cè)一個(gè) ‘在請(qǐng)求的url匹配后視圖函數(shù)執(zhí)行前,把環(huán)境中的某些變量換個(gè)位置存儲(chǔ)的’ 函數(shù)
# url_defaults(self, f) 和 url_value_preprocessor(self, f) 的使用 from flask import Blueprint, render_template profile = Blueprint("profile", __name__, url_prefix="/方法:") @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): 匹配路由,返回視圖函數(shù)或者錯(cuò)誤處理函數(shù)的返回值,并且檢測(cè)是否為option請(qǐng)求,如果是則構(gòu)造默認(rèn)的 ‘options response’ 響應(yīng)。構(gòu)造過(guò)程首先是 Request uri 所支持的方法集(get、post、等),然后更新 Response 中 allow 屬性(set類型),最后返回Response對(duì)象,若不是option請(qǐng)求則執(zhí)行視圖函數(shù);
make_response(self, rv): rv是視圖函數(shù)的返回值,在python3中,rv可以使一個(gè)元組(body、status、headers)、Response類對(duì)象、 或者一個(gè)返回Response類對(duì)象的回調(diào)函數(shù)。這個(gè)函數(shù)的功能就是把視圖函數(shù)返回的 status headers 綁定到 Response;
create_url_adapter(self, request):url_map 適配器。對(duì)werkzeug的Map的bind和bind_to_environ兩個(gè)方法進(jìn)行了封裝。bind: 綁定一個(gè)主機(jī)地址,并返回MapAdapter對(duì)象 ; bind_to_environ( 將MAP綁定到WSGI環(huán)境中,并返回MapAdapter對(duì)象(參數(shù)script_name在進(jìn)行重定向時(shí)會(huì)用到);
try_trigger_before_first_request_functions(self):在該實(shí)例第一個(gè)請(qǐng)求到達(dá)時(shí)把當(dāng)前線程加鎖,然后依次執(zhí)行被 before_first_request(self, f)裝飾過(guò)得函數(shù),然后釋放鎖并把_got_first_request置為True,再次就直接return;
preprocess_request(self) :該函數(shù)就是執(zhí)行被url_value_preprocessor 和 before_request裝飾過(guò)的函數(shù);
full_dispatch_request(self): 先執(zhí)行try_trigger_before_first_request_functions,然后執(zhí)行preprocess_request,若before_request中的函數(shù)有返回值則為其構(gòu)造Response,然后跳過(guò)排在此函數(shù)后邊的函數(shù)包括視圖函數(shù),若before_request的函數(shù)都返回的None或沒(méi)有函數(shù)就執(zhí)行 dispatch_request(self);
inject_url_defaults(self, error, endpoint, values): 執(zhí)行被"url_defaults" 裝飾的函數(shù)
process_response(self, response): 同preprocess_request(self)
通過(guò)查看源碼了解到的幾個(gè)工具:click 包 把python代碼打包成命令行工具
mimetypes 包 查看文件類型
itsdangerous 簽名 序列化字符串
參考資料:
Flask的核心機(jī)制!關(guān)于請(qǐng)求處理流程和上下文
Werkzeug(Flask)之Local、LocalStack和LocalProxy
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/45028.html
摘要:我們的論壇項(xiàng)目就使用了該框架。此外,麥子學(xué)院也有一個(gè)入門視頻教程,一共小時(shí)的視頻教程,涵蓋開(kāi)發(fā)的方方面面,包括環(huán)境的搭建,語(yǔ)法介紹,項(xiàng)目結(jié)構(gòu)的組織,全球化,單元測(cè)試等內(nèi)容。博客地址更多閱讀的機(jī)制三個(gè)框架的對(duì)比 前面兩篇文章中我們已經(jīng)了解 Web(HTTP)服務(wù)器,Web應(yīng)用程序,Web框架,WSGI這些 Python Web 開(kāi)發(fā)中的概念。我們知道,Web框架通過(guò)將不同Web應(yīng)用程序中...
摘要:注模板包含不存在此問(wèn)題。在模板中,使用過(guò)濾器顯式地標(biāo)記一個(gè)字符串為安全的臨時(shí)地完全禁用自動(dòng)轉(zhuǎn)義系統(tǒng)。通過(guò)在聲明中直接添加或,當(dāng)前的上下文可以傳遞到模板,而且不會(huì)自動(dòng)禁用緩存。 官網(wǎng):http://jinja.pocoo.org/docs/dev/中文文檔:http://docs.jinkan.org/docs/j...當(dāng)前版本2.8 安裝:pip install Jinja2Flask...
摘要:通過(guò)的核心特性,函數(shù)可實(shí)現(xiàn)這種效果僅調(diào)用函數(shù)并不能把消息顯示出來(lái),程序使用的模板要渲染這些消息。注意在模板中使用循環(huán)是因?yàn)樵谥暗恼?qǐng)求循環(huán)中每次調(diào)用函數(shù)時(shí)都會(huì)生成一個(gè)消息,所以可能有多個(gè)消息在排隊(duì)等待顯示。 五、表單 1、Flask-WTF 擴(kuò)展 Flask-WTF 及其依賴可使用 pip 安裝: (venv) $ pip install flask-wtf 2、跨站請(qǐng)求偽造保護(hù) 【設(shè)...
閱讀 2012·2023-04-25 14:49
閱讀 3268·2021-09-30 09:47
閱讀 3408·2021-09-06 15:00
閱讀 2368·2019-08-30 13:16
閱讀 1583·2019-08-30 10:48
閱讀 2805·2019-08-29 15:11
閱讀 1446·2019-08-26 14:06
閱讀 1816·2019-08-26 13:30