flask?route對協(xié)議作用及設(shè)計(jì)思路
引言
本文主要梳理了flask
源碼中route
的設(shè)計(jì)思路。
首先,從WSGI
協(xié)議的角度介紹flask route
的作用;
其次,詳細(xì)講解如何借助werkzeug
庫的Map
、Rule
實(shí)現(xiàn)route
;
最后,梳理了一次完整的http請求中route
的完整流程。
flask route 設(shè)計(jì)思路
源碼版本說明
本文參考的是flask 0.5
版本的代碼。
flask 0.1
版本的代碼非常短,只有600多行,但是這個(gè)版本缺少blueprint
機(jī)制。
因此,我參考的是0.5版本。
flask route示例
直接使用flask
官方文檔中的例子
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World!' @app.route('/post/') def show_post(post_id): # show the post with the given id, the id is an integer return 'Post %d' % post_id if __name__ == '__main__': app.run()
此例中,使用app.route
裝飾器,完成了以下兩個(gè)url
與處理函數(shù)的route
:
{ '/': hello_world, '/post/' : show_post }
這樣做的效果為:
當(dāng)http請求的url為'/'時(shí),flask會調(diào)用hello_world函數(shù);
當(dāng)http請求的url為'/post/<某整數(shù)值>'(例如/post/32)時(shí),flask會調(diào)用show_post函數(shù);
flask route的作用
從上面的示例中其實(shí)可以明白:flask route的作用就是建立url與處理函數(shù)的映射。
WSGI協(xié)議將處理請求的組件按照功能及調(diào)用關(guān)系分成了三種:server, middleware, application。
其中,server可以調(diào)用middleware和application,middleware可以調(diào)用application。
符合WSGI的框架對于一次http請求的完整處理過程為:
server讀取解析請求,生成environ和start_response,然后調(diào)用middleware;
middleware完成自己的處理部分后,可以繼續(xù)調(diào)用下一個(gè)middleware或application,形成一個(gè)完整的請求鏈;
application位于請求鏈的最后一級,其作用就是生成最終的響應(yīng)。
http服務(wù)器(比如,nginx)--> WSGI server(比如gunicorn,SimpleHttpServer)-->middleware--> middleware--> ... -->application
如果接觸過Java Web 開發(fā)的人可能會立刻發(fā)現(xiàn),這與servlet中的middleware機(jī)制是完全一致的。
特別重要的:
在上一小節(jié)的示例中app = Flask(__name__)
創(chuàng)建了一個(gè)middleware,
而這個(gè)middleware的核心作用是進(jìn)行請求轉(zhuǎn)發(fā)(request dispatch)。
上面這句話非常重要,請?jiān)谛睦镏貜?fù)一百遍。
上面這句話非常重要,請?jiān)谛睦镏貜?fù)一百遍。
上面這句話非常重要,請?jiān)谛睦镏貜?fù)一百遍。
進(jìn)行請求轉(zhuǎn)發(fā)的前提就是能夠建立url與處理函數(shù)之間的映射關(guān)系,即route
功能。
因此,在flask
中,route是Flask類的一個(gè)裝飾器。
flask route的實(shí)現(xiàn)思路
通過上一小節(jié),我們知道以下兩點(diǎn):
flask route
是url與處理函數(shù)的映射關(guān)系;- 在http請求時(shí),
Flask
這個(gè)middleware
負(fù)責(zé)完成對url對應(yīng)的處理函數(shù)的調(diào)用;
那么,如果是我們自己來實(shí)現(xiàn)route
,思路也很簡單:
- 建立一個(gè)類
Flask
,這個(gè)類是一個(gè)middleware,并且有一個(gè)字典型的成員變量url_map
; url_map = {url : function}
- 當(dāng)http請求時(shí),進(jìn)行request dispatch:根據(jù)url,從url_map中找到
function
,然后調(diào)用function; - 調(diào)用后續(xù)的middleware或application,并把function的結(jié)果傳遞下去。
flask的實(shí)現(xiàn)思路也是這樣的。
class Flask(object): def __init__(self): self.url_map = {} # 此處定義保存url與處理函數(shù)的映射關(guān)系 def __call__(self, environ, start_response): # 根據(jù)WSGI協(xié)議,middleware必須是可調(diào)用對象 self.dispatch_request() # Flask的核心功能 request dispatch return application(environ, start_response) #最后調(diào)用下一級的application def route(self, rule): # Flask使用裝飾器來完成url與處理函數(shù)的映射關(guān)系建立 def decorator(f): # 簡單,侵入小,優(yōu)雅 self.url_map[rule] = f return f return decorator def dispath_request(self): url = get_url_from_environ() #解析environ獲得url return self.url_map[url]() #從url_map中找到對應(yīng)的處理函數(shù),并調(diào)用
至此, 一個(gè)簡單的Flask
middleware的骨架就完成了。
上面的Flask
類主要功能包括:
- 符合WSGI協(xié)議的middleware:可被調(diào)用,并且可以調(diào)用application
- 能夠保存url與處理函數(shù)的映射信息
- 能夠根據(jù)url找到處理函數(shù)并調(diào)用(即,request dispatch)
當(dāng)然,在實(shí)際中,不可能這么簡單,但是基本思路是一致的。
werkzeug庫中的Map與Rule在Flask中的應(yīng)用
需要指出,上面實(shí)現(xiàn)的最簡單的Flask
類還是有很多問題的。
比如,HTTP請求中相同的url,不同的請求方法,比如GET,POST如果對應(yīng)不同的處理函數(shù),該如何處理?
flask使用了werkzeug
庫中的Map
和Rule
來管理url與處理函數(shù)映射關(guān)系。
首先需要簡單了解一下Map
和Rule
的作用:
在werkzeug
中,Rule
的主要作用是保存了一組url
,endpoint
,methods
關(guān)系:
每個(gè)(url, endpoint, methods)都有一個(gè)對應(yīng)的Rule對象:
其實(shí)現(xiàn)如下:
class Rule(object): def __init__(self, url, endpoint, methods): self.rule = url self.endpoint = endpoint self.methods = methods
這里需要解釋一下endpoint
:
前面說過:url與其處理函數(shù)可以使用一個(gè)字典來實(shí)現(xiàn):{url: function}
flask
在實(shí)現(xiàn)的時(shí)候,在中間加了一個(gè)中介endpoint
,于是,url與處理函數(shù)的映射變成了這樣:
url-->endpoint-->function #一個(gè)url對應(yīng)一個(gè)endpoint,一個(gè)endpoint對應(yīng)一個(gè)function {url: endpoint} # 保存url與endpoint之間的關(guān)系 {endpoint: function} #保存endpoint與function之間的關(guān)系
于是,剛才我們實(shí)現(xiàn)的簡單的flask
骨架中{url: function}
的字典,就變成了{endpoint: function}
,而{url: endpoint}
這個(gè)映射關(guān)系就需要借助Map
和Rule
這兩個(gè)類來完成。
可以發(fā)現(xiàn):endpoint
就是url和處理函數(shù)映射關(guān)系中的一個(gè)中介,所以,它可以是任何可以用作字典鍵的值,比如字符串。
但是在實(shí)際使用中endpoint
,一般endpoint
均為字符串,并且默認(rèn)情況下:
- 如果是通過
Flask.route
裝飾器建立的映射關(guān)系,那么endpoint
就是處理函數(shù)的函數(shù)名; - 如果是通過
blueprint
建立的映射關(guān)系,那么endpoint
是blueprint名.處理函數(shù)名;
因?yàn)?,每建立一個(gè)url-->endpoint-->function
關(guān)系就會創(chuàng)建一個(gè)Rule
對象,所以,會有很多Rule
對象存在。
Map
的作用則是保存所有Rule
對象。
所以,一般情況下Map
的用法如下:
m = Map([ Rule('/', endpoint='index'), Rule('/downloads/', endpoint='downloads/index'), Rule('/downloads/', endpoint='downloads/show') ])
在flask的源碼中
class Flask(object): def __init__(self): self.url_map = Map() # url_map為保存所有Rule關(guān)系的容器Map self.view_functions = {} # view_functions保存endpoint-->function
- 成員變量
url_map
保存所有的(url, endpoint, method)
關(guān)系 - 成員變量
view_functions
保存所有的{endpoint, function}關(guān)系
所以,對于一個(gè)url,只要能找到(url,endpoint,method)
,就能根據(jù)endpoint
找到對應(yīng)的function
。
route的完整流程
首先,建立Flask
對象:
app = Flask(__name__)
然后,建立url
與function
之間的映射關(guān)系:
@app.route('/') def hello_world(): return 'Hello World!'
在裝飾器route
中,創(chuàng)建(url, endpoint, method)
和{endpoint: function}
兩組映射關(guān)系:
if endpoint is None: endpoint = view_func.__name__ # 默認(rèn)使用響應(yīng)函數(shù)名作為endpoint self.url_map.add(Rule(url, endpoint, method)) # 保存(url, endpoint, method)映射關(guān)系 self.view_functions[endpoint] = view_func # 保存{endpoint: function}映射關(guān)系
這樣,就完成了對url和響應(yīng)函數(shù)的映射關(guān)系。
下一步,調(diào)用WSGI server響應(yīng)http請求,在文章開始的示例中使用:
app.run()
調(diào)用python
標(biāo)準(zhǔn)庫提供的WSGI server,在實(shí)際使用時(shí),可能是gunicorn
或uwsgi
。
不論server是什么,最終都會調(diào)用Flask.__call__
函數(shù)。這個(gè)函數(shù)完成request dispatch的任務(wù)。
對于request dispatch而言,首先根據(jù)請求,解析environ,得到url,然后調(diào)用Map.match
函數(shù),這個(gè)函數(shù)會最終找到預(yù)先保存的(url, endpoint, method)
映射,然后返回(endpoint, url請求參數(shù)),由于得到了endpoint,然后,可以從Flask.view_functions
中直接取到對應(yīng)的響應(yīng)函數(shù),所以,可以直接進(jìn)行函數(shù)調(diào)用
self.view_functions[endpoint](url請求參數(shù))
至此,就完成了完整的route
。
總結(jié)
flask
的Flask
類是WSGI
的dispatch middleware
;
Flask
的url_map
保存所有的(url, endpoint, method)映射關(guān)系;
Flask
的view_functions
保存所有的{endpoint: function}映射關(guān)系;
dispath request
就是根據(jù)url找到endpoint,再根據(jù)endpoint找到function,最后調(diào)用function的過程
以上就是flask route對協(xié)議作用及設(shè)計(jì)思路的詳細(xì)內(nèi)容,更多關(guān)于flask route協(xié)議設(shè)計(jì)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Pyecharts 中Geo函數(shù)常用參數(shù)的用法說明
這篇文章主要介紹了Pyecharts 中Geo函數(shù)常用參數(shù)的用法說明,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02python itchat實(shí)現(xiàn)調(diào)用微信接口的第三方模塊方法
這篇文章主要介紹了python itchat實(shí)現(xiàn)調(diào)用微信接口的第三方模塊方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06Python最基本的數(shù)據(jù)類型以及對元組的介紹
這篇文章主要介紹了Python最基本的數(shù)據(jù)類型以及對元組的介紹,來自于IBM官方網(wǎng)站技術(shù)文檔,需要的朋友可以參考下2015-04-04使用Python機(jī)器學(xué)習(xí)降低靜態(tài)日志噪聲
今天小編就為大家分享一篇關(guān)于使用Python和機(jī)器學(xué)習(xí)的靜態(tài)日志噪聲的文章,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-09-09