版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、淺談Flask基本工作流程前置技能 - WSGI在具體讀源碼之前,這里先需要說一個(gè)概念,什么是WSGI。WSGI,全稱 Web Server Gateway Interface,或者 Python Web Server Gateway Interface ,是為 Python 語(yǔ)言定義的 Web 服務(wù)器和 Web 應(yīng)用程序或框架之間的一種簡(jiǎn)單而通用的接口。自從 WSGI 被開發(fā)出來以后,許多其它語(yǔ)言中也出現(xiàn)了類似接口。WSGI 的官方定義是,the Python Web Server Gateway Interface。從名字就可以看出來,這東西是一個(gè)Gateway,也就是網(wǎng)關(guān)。網(wǎng)關(guān)的作用就是
2、在協(xié)議之間進(jìn)行轉(zhuǎn)換。WSGI 是作為 Web 服務(wù)器與 Web 應(yīng)用程序或應(yīng)用框架之間的一種低級(jí)別的接口,以提升可移植 Web 應(yīng)用開發(fā)的共同點(diǎn)。WSGI 是基于現(xiàn)存的 CGI 標(biāo)準(zhǔn)而設(shè)計(jì)的。很多框架都自帶了 WSGI server ,比如 Flask,webpy,Django、CherryPy等等。當(dāng)然性能都不好,自帶的 web server 更多的是測(cè)試用途,發(fā)布時(shí)則使用生產(chǎn)環(huán)境的 WSGI server或者是聯(lián)合 nginx 做 uwsgi 。在網(wǎng)上搜過WSGI的人應(yīng)該會(huì)看到一個(gè)圖,左邊是Server,右邊是APP,中間有一個(gè)連接紐帶是WSGI。不過,我看了源碼以后的理解和這個(gè)有些不同,
3、我個(gè)人覺得,實(shí)際上不應(yīng)該單獨(dú)寫一個(gè)APP,因?yàn)?,這個(gè)WSGI的使用方法實(shí)際上也是包含在APP里面的,最右端的app實(shí)際上應(yīng)該指的是邏輯功能,包括URL和view function的對(duì)應(yīng)關(guān)系。所以我個(gè)人的理解如下自己畫的圖WSGI其實(shí)是作為一個(gè)接口,來接受Server傳遞過來的信息, 然后通過這個(gè)接口調(diào)用后臺(tái)app里的view function進(jìn)行響應(yīng)。WSGI具體的功能上面講到了WSGI可以起到一個(gè)接口的功能,前面對(duì)接服務(wù)器,后面對(duì)接app的具體功能我們先來看看最簡(jiǎn)單的一個(gè)wsgi_app的實(shí)現(xiàn)python view plain copy 在CODE上查看代碼片派生到我的代碼片def appl
4、ication(environ, start_response): #一個(gè)符合wsgi協(xié)議的應(yīng)用程序?qū)懛☉?yīng)該接受2個(gè)參數(shù) start_response('200 OK', ('Content-Type', 'text/html') #environ為http的相關(guān)信息,如請(qǐng)求頭等 start_response則是響應(yīng)信息 return b'<h1>Hello, web!</h1>' #return出來是響應(yīng)內(nèi)容 但是,作為app本身,你就算啟動(dòng)了程序,你也沒辦法給application傳遞參數(shù)?所以,實(shí)際上
5、,調(diào)用application和傳遞2個(gè)參數(shù)的動(dòng)作,是服務(wù)器來進(jìn)行的,比如Gunicorn.而這個(gè)叫做application的東西,在Flask框架內(nèi)部的名字,叫做wsgi_app,請(qǐng)看下面章節(jié)的源碼。Flask和WSGI生成Flask實(shí)例我們?cè)賮砜聪律蒮lask應(yīng)用的操作寫法,用過的人肯定都非常熟悉。python view plain copy 在CODE上查看代碼片派生到我的代碼片from flask import Flask app = Flask(_name_) #生成app實(shí)例 app.route('/') def index(): return 'Hello
6、 World' 這樣,一個(gè)flask app就生成了但是這里有一個(gè)概念必須要搞清楚,就是當(dāng)你的gunicorn收到http請(qǐng)求,去調(diào)用app的時(shí)候,他實(shí)際上是用了Flask 的 _call_方法,這點(diǎn)非常重要!因?yàn)開call_方法怎么寫,決定了你整個(gè)流程從哪里開始。那我們來看下Flask類的_call_方法的源碼python view plain copy 在CODE上查看代碼片派生到我的代碼片class Flask(_PackageBoundObject): #Flask類 #中間省略一些代碼 def _call_(self, environ, start_response): #F
7、lask實(shí)例的_call_方法 """Shortcut for :attr:wsgi_app.""" return self.wsgi_app(environ, start_response) #注意他的return,他返回的時(shí)候,實(shí)際上是調(diào)用了wsgi_app這個(gè)功能 如此一來,我們便知道,當(dāng)http請(qǐng)求從server發(fā)送過來的時(shí)候,他會(huì)啟動(dòng)_call_功能,最終實(shí)際是調(diào)用了wsgi_app功能并傳入environ和start_responseFlask的wsgi_app定義python view plain copy 在CODE上
8、查看代碼片派生到我的代碼片class Flask(_PackageBoundObject): #中間省略一些代碼 #請(qǐng)注意函數(shù)的說明,說得非常準(zhǔn)確,這個(gè)wsgi_app是一個(gè)真正的WSGI應(yīng)用 def wsgi_app(self, environ, start_response): #他扮演的是一個(gè)中間角色 """The actual WSGI application. This is not implemented in _call_ so that middlewares can be applied without losing a reference to
9、 the class. So instead of doing this: app = MyMiddleware(app) It's a better idea to do this instead: app.wsgi_app = MyMiddleware(app.wsgi_app) Then you still have the original application object around and can continue to call methods on it. :param environ: a WSGI environment :param start_respon
10、se: a callable accepting a status code, a list of headers and an optional exception context to start the response """ ctx = self.request_context(environ) ctx.push() error = None try: try: response = self.full_dispatch_request() #full_dispatch_request起到了預(yù)處理和錯(cuò)誤處理以及分發(fā)請(qǐng)求的作用 except Excepti
11、on as e: error = e response = self.make_response(self.handle_exception(e) #如果有錯(cuò)誤發(fā)生,則生成錯(cuò)誤響應(yīng) return response(environ, start_response) #如果沒有錯(cuò)誤發(fā)生,則正常響應(yīng)請(qǐng)求,返回響應(yīng)內(nèi)容 finally: if self.should_ignore_error(error): error = None ctx.auto_pop(error) Ok, 這個(gè)wsgi_app的函數(shù)定義,基本上包含了整個(gè)流程的功能WSGI_APP的內(nèi)部流程第一步:生成request請(qǐng)求對(duì)象和請(qǐng)
12、求上下文環(huán)境首先,你會(huì)看到ctx = self.request_context(environ)的語(yǔ)句,這個(gè)涉及到Flask使用了請(qǐng)求上下文和應(yīng)用上下文的概念,結(jié)構(gòu)為棧結(jié)構(gòu),這部分比較難,后面第二篇會(huì)單獨(dú)寫。這里只需要理解為,上面語(yǔ)句產(chǎn)生的所用是生成了一個(gè)request請(qǐng)求對(duì)象以及包含請(qǐng)求信息在內(nèi)的request context第二部:請(qǐng)求進(jìn)入預(yù)處理,錯(cuò)誤處理及請(qǐng)求轉(zhuǎn)發(fā)到響應(yīng)的過程進(jìn)入wsgi_app的函數(shù)內(nèi)部,生成了request對(duì)象和上下文環(huán)境之后,進(jìn)入到trypython view plain copy 在CODE上查看代碼片派生到我的代碼片response = self.full_di
13、spatch_request() 我們看到,響應(yīng)被賦值成了full_dispatch_request()方法的返回內(nèi)容,所以我們來看一下full_dispatch_request方法python view plain copy 在CODE上查看代碼片派生到我的代碼片class Flask(_PackageBoundObject): #此處省略一些代碼 def full_dispatch_request(self): """Dispatches the request and on top of that performs request pre and postp
14、rocessing as well as HTTP exception catching and error handling. . versionadded: 0.7 """ self.try_trigger_before_first_request_functions() #進(jìn)行發(fā)生真實(shí)請(qǐng)求前的處理 try: request_started.send(self) #socket部分的操作 rv = self.preprocess_request() #進(jìn)行請(qǐng)求的預(yù)處理 if rv is None: rv = self.dispatch_request() ex
15、cept Exception as e: rv = self.handle_user_exception(e) response = self.make_response(rv) response = cess_response(response) request_finished.send(self, response=response) return response 他首先會(huì)觸發(fā) try_trigger_before_first_request_function()方法 在方法內(nèi)部 ->會(huì)觸發(fā) _got_first_request 屬性,這個(gè)屬性的返回值是True或
16、者False. True的話就代表了程序開始處理請(qǐng)求了.來看看 try_trigger_before_first_request_function()的代碼,他的目的是,最后將_got_first_request屬性置為True.python view plain copy 在CODE上查看代碼片派生到我的代碼片class Flask(_PackageBoundObject): #省略一些代碼 def try_trigger_before_first_request_functions(self): """Called before each request an
17、d will ensure that it triggers the :attr:before_first_request_funcs and only exactly once per application instance (which means process usually). :internal: """ if self._got_first_request: return with self._before_request_lock: if self._got_first_request: return for func in self.befor
18、e_first_request_funcs: func() self._got_first_request = True 再來看看_got_first_request 的定義,他的默認(rèn)值是False他的定義中可以明顯看到, if the application started,this attribute is set to True.python view plain copy 在CODE上查看代碼片派生到我的代碼片class Flask(_PackageBoundObject): #省略一些代碼 property def got_first_request(self): "&qu
19、ot;"This attribute is set to True if the application started handling the first request. . versionadded: 0.8 """ return self._got_first_request 接著,當(dāng)_got_first_request 屬性被設(shè)置完以后,我們就需要再次回到 full_dispatch_request函數(shù)內(nèi)部,繼續(xù)往下走下面一段代碼是request_started.send(),他是繼承自signal模塊,大致作用是進(jìn)行socket部分的功能,
20、暫時(shí)不詳細(xì)追溯。preprocess_request()方法的話,主要是進(jìn)行flask的hook鉤子, before_request功能的實(shí)現(xiàn),也就是在真正發(fā)生請(qǐng)求之前,有些事情需要提前做Flask一共有4個(gè)hook鉤子,另外再寫吧隨后,繼續(xù)往下走,來到了一個(gè)至 關(guān) 重 要的功能 dispatch_request()python view plain copy 在CODE上查看代碼片派生到我的代碼片try: request_started.send(self) rv = self.preprocess_request() if rv is None: rv = self.dispatch_re
21、quest() 為什么說至關(guān)重要,因?yàn)橐粋€(gè)http請(qǐng)求到了這里,實(shí)際上已經(jīng)完成了從wsgi部分的過渡,進(jìn)入到了尋找響應(yīng)的階段了,一個(gè)請(qǐng)求通過url進(jìn)來以后,app怎么知道要如何響應(yīng)呢?就是通過dispatch_request方法來進(jìn)行請(qǐng)求判定和分發(fā)。第三步:請(qǐng)求分發(fā) dispatch_request來看源碼python view plain copy 在CODE上查看代碼片派生到我的代碼片class Flask(_PackageBoundObject): #省略一些代碼 def dispatch_request(self): #看函數(shù)定義,matches the URL and returns
22、 the value of the view or error. """Does the request dispatching. Matches the URL and returns the return value of the view or error handler. This does not have to be a response object. In order to convert the return value to a proper response object, call :func:make_response. . versio
23、nchanged: 0.7 This no longer does the exception handling, this code was moved to the new :meth:full_dispatch_request. """ req = _request_ctx_stack.top.request if req.routing_exception is not None: self.raise_routing_exception(req) rule = req.url_rule # if we provide automatic options
24、for this URL and the # request came with the OPTIONS method, reply automatically if getattr(rule, 'provide_automatic_options', False) and req.method = 'OPTIONS': return self.make_default_options_response() # otherwise dispatch to the handler for that endpoint return self.view_functio
25、nsrule.endpoint(*req.view_args) #最終進(jìn)入view_functions,取出url對(duì)應(yīng)的視圖函數(shù)的返回值 中間不需要過多考慮,req = _request_ctx_stack.top.request 可以暫時(shí)理解為,將請(qǐng)求對(duì)象賦值給req這里先簡(jiǎn)單講下,每個(gè)url進(jìn)來以后,他都會(huì)對(duì)應(yīng)一個(gè)view_function比如下面的一個(gè)簡(jiǎn)單視圖函數(shù),路徑 '/' 對(duì)應(yīng)的是index函數(shù)但是,實(shí)際上當(dāng)中是分2步走的,第一步是'/'對(duì)應(yīng)的endpoint為'index' ,第二部是endpoint 'index'
26、 對(duì)應(yīng)到index()視圖函數(shù)這個(gè)也是放在第二篇文章里面具體寫flask 路由的實(shí)現(xiàn),這里暫時(shí)可以忽略中間步驟,只要知道URL->VIEW FUNCTION的邏輯步驟就okpython view plain copy 在CODE上查看代碼片派生到我的代碼片app.route('/') def index(): return 'Hello world' 另外說下view_functions 是一個(gè)字典形式,他的key和value的關(guān)系是endpoint -> view function所以每個(gè)有效的URL進(jìn)來,都能找到他對(duì)應(yīng)的視圖函數(shù)view func
27、tion,取得返回值并賦值給 rv比如上面簡(jiǎn)單的index,他取得的就是 'Hello world' 值請(qǐng)求分發(fā)完成后,已經(jīng)取得了返回的值,再看下一步是如何做我們?cè)俅位氐?full_dispatch_request方法內(nèi)往下走h(yuǎn)tml view plain copy 在CODE上查看代碼片派生到我的代碼片response = self.make_response(rv) response = cess_response(response) request_finished.send(self, response=response) return response
28、 這時(shí)候,通過make_response函數(shù),將剛才取得的 rv 生成響應(yīng),重新賦值response再通過process_response功能主要是處理一個(gè)after_request的功能,比如你在請(qǐng)求后,要把數(shù)據(jù)庫(kù)連接關(guān)閉等動(dòng)作,和上面提到的before_request對(duì)應(yīng)和類似。之后再進(jìn)行request_finished.send的處理,也是和socket處理有關(guān),暫時(shí)不詳細(xì)深入。之后返回新的response對(duì)象這里特別需要注意的是,make_response函數(shù)是一個(gè)非常重要的函數(shù),他的作用是返回一個(gè)response_class的實(shí)例對(duì)象,也就是可以接受environ和start_rep
29、onse兩個(gè)參數(shù)的對(duì)象非 常 重 要!Converts the return value from a view function to a real response object that is an instance of :attr:response_classpython view plain copy 在CODE上查看代碼片派生到我的代碼片class Flask(_PackageBoundObject): #注意函數(shù)說明,converts the return value from view function to a real response object #省略一部分代碼 d
30、ef make_response(self, rv): """Converts the return value from a view function to a real response object that is an instance of :attr:response_class. The following types are allowed for rv: . tabularcolumns: |p3.5cm|p9.5cm| = = :attr:response_class the object is returned unchanged :cla
31、ss:str a response object is created with the string as body :class:unicode a response object is created with the string encoded to utf-8 as body a WSGI function the function is called as WSGI application and buffered as response object :class:tuple A tuple in the form (response, status, headers) or
32、(response, headers) where response is any of the types defined here, status is a string or an integer and headers is a list or a dictionary with header values. = = :param rv: the return value from the view function . versionchanged: 0.9 Previously a tuple was interpreted as the arguments for the res
33、ponse object. """ status_or_headers = headers = None if isinstance(rv, tuple): rv, status_or_headers, headers = rv + (None,) * (3 - len(rv) if rv is None: raise ValueError('View function did not return a response') if isinstance(status_or_headers, (dict, list): headers, status_or_headers = status_or_headers, None if not isinstance(rv, self.response_class): # When we create a response object di
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 財(cái)富卡轉(zhuǎn)讓協(xié)議書
- 肺癌的診斷及治療
- 針灸治療頸椎病腰椎病
- 藝術(shù)活動(dòng):我的媽媽真漂亮
- 《J類船用自閉式油位計(jì)》
- 廣西玉林市北流市2024-2025學(xué)年七年級(jí)上學(xué)期11月期中數(shù)學(xué)試題(含答案)
- 2025新課改-高中物理-選修第1冊(cè)(21講)18 B全反射 中檔版含答案
- 簡(jiǎn)單糖尿病視網(wǎng)膜病變
- 氣浮電主軸行業(yè)相關(guān)投資計(jì)劃提議
- 在線編輯軟件相關(guān)行業(yè)投資規(guī)劃報(bào)告
- Specification-原材料規(guī)格書模板
- 實(shí)驗(yàn)室課外向?qū)W生開放計(jì)劃
- 科技特派員工作調(diào)研報(bào)告
- 2021年電力公司創(chuàng)一流工作會(huì)議講話
- 中波廣播發(fā)送系統(tǒng)概述
- 縣疾控中心中層干部競(jìng)聘上崗實(shí)施方案
- 急性心肌梗死精美PPt完整版
- 畢業(yè)設(shè)計(jì)(論文)基于三菱PLC的交通燈模擬控制
- (完整版)offer模板范本.docx
- 物業(yè)日常巡查記錄表.doc
- 門技術(shù)參數(shù)[圖文借鑒]
評(píng)論
0/150
提交評(píng)論