1. 程式人生 > >Flask中'endpoint'(端點)的理解

Flask中'endpoint'(端點)的理解

分享圖片 參考 改變 實例 因此 name point ini comment

Flask路由是如何工作的

整個flask框架(及以Werkzeug類庫為基礎構建的應用)的程序理念是把URL地址映射到你想要運行的業務邏輯上(最典型的就是視圖函數),例如:

@app.route(‘/greeting/<name>‘)
def give_greeting(name):
    return ‘Hello, {0}!‘.format(name)
  • 1
  • 2
  • 3

註意,add_url_rule函數實現了同樣的目的,只不過沒有使用裝飾器,因此,下面的程序是等價的:

# 擡頭沒有使用路由裝飾器,我們在最後用另一種方法添加路由.
def give_greeting(name):
    return ‘Hello, {0}!‘.format(name)

app.add_url_rule(‘/greeting/<name>‘, ‘give_greeting‘, give_greeting)
  • 1
  • 2
  • 3
  • 4
  • 5

備註:add_url_rule()中3個參數依次是rule、view_func、endpoint.

假設www.example.org站點定義了以上視圖,用戶在瀏覽器中輸入以下地址

http://www.example.org/greeting/Mark
  • 1

Flask的工作就是捕捉這個URL地址,弄清用戶想要做什麽,並在眾多的Python函數中匹配一個可以處理它的函數,回到我們的實例中,URL地址就是

/greeting/Mark
  • 1

拿著這個地址到路由表中做匹配,flask發現這個地址指向了give_greeting函數。

然而,當我們用這種最常用的方法創建視圖時,flask卻向我們隱藏了一些其他的細節信息。在這個場景中,flask並沒有直接從URL地址跳轉到應該響應它請求的視圖函數上:

URL (http://www.example.org/greeting/Mark) 被視圖函數處理 ("give_greeting"函數)
  • 1

事實上,這裏還有另一個步驟–把URL地址映射到端點上(URL**–>endpoint–>**viewfunction):

URL (http://www.example.org/greeting/Mark) 映射到端點"give_greeting"上.
指向端點"give_greeting"的請求被視圖函數"give_greeting"處理.
  • 1
  • 2

從根本上來說,端點就是程序中一組邏輯處理單元的ID,該ID對應的代碼決定了對此ID請求應該作出何種響應。通常,端點與視圖函數同名,但是你也可以修改它,例如:

@app.route(‘/greeting/<name>‘, endpoint=‘say_hello‘)
def give_greeting(name):
    return ‘Hello, {0}!‘.format(name)
  • 1
  • 2
  • 3

現在就成了這樣:

URL (http://www.example.org/greeting/Mark) 映射到端點"say_hello"上.
指向端點"say_hello"的請求被視圖函數"give_greeting"處理.
  • 1
  • 2

Endpoint有什麽作用

端點通常用作反向查詢URL地址(viewfunction**–>endpoint–>**URL)。例如,在flask中有個視圖,你想把它關聯到另一個視圖上(或從站點的一處連接到另一處)。不用去千辛萬苦的寫它對應的URL地址,直接使用URL_for()就可以啦:

@app.route(‘/‘)
def index():
    print url_for(‘give_greeting‘, name=‘Mark‘) # 打印出 ‘/greeting/Mark‘

@app.route(‘/greeting/<name>‘)
def give_greeting(name):
    return ‘Hello, {0}!‘.format(name)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

備註:url_for()give_greeting是端點名.

這樣做是大有裨益的:我們可以隨意改變應用中的URL地址,卻不用修改與之關聯的資源的代碼。

為何要多此一舉

那麽問題來了:為何要多此一舉,為何要先把URL映射到端點上,再通過端點映射到視圖函數上,為何不跳過中間的這個步驟?
原因就是采用這種方法能夠使程序更高、更快、更強。例如藍本。藍本允許我們把應用分割為一個個小的部分,現在admin藍本中含有超級管理員級的資源,user藍本中則含有用戶一級的資源。
藍本允許咱們把應用分割為一個個以命名空間區分的小部分:
main.py:

from flask import Flask, Blueprint
from admin import admin
from user import user

app = Flask(__name__)
app.register_blueprint(admin, url_prefix=‘admin‘)
app.register_blueprint(user, url_prefix=‘user‘)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

admin.py:

admin = Blueprint(‘admin‘, __name__)

@admin.route(‘/greeting‘)
def greeting():
    return ‘Hello, administrative user!‘
  • 1
  • 2
  • 3
  • 4
  • 5

user.py:

user = Blueprint(‘user‘, __name__)
@user.route(‘/greeting‘)
def greeting():
    return ‘Hello, lowly normal user!‘
  • 1
  • 2
  • 3
  • 4

註意,在兩個藍本中路由地址‘/greeting’的函數都叫"greeting"。如果我想調用admin對應的greeting函數,我不能說“我想要greeting”,因為這裏還有一個user對應的greeting函數。端點這時就發揮作用了:指定一個藍本名稱作為端點的一部分–通過這種方式端點實現了對命名空間的支持。所以,我們可以這樣寫:

print url_for(‘admin.greeting‘) # Prints ‘/admin/greeting‘
print url_for(‘user.greeting‘) # Prints ‘/user/greeting‘
  • 1
  • 2

來發實例

from flask import Flask, url_for

app = Flask(__name__)

# We can use url_for(‘foo_view‘) for reverse-lookups in templates or view functions
@app.route(‘/foo‘)
def foo_view():
    pass

# We now specify the custom endpoint named ‘bufar‘. url_for(‘bar_view‘) will fail!
@app.route(‘/bar‘, endpoint=‘bufar‘)
def bar_view():
    pass

with app.test_request_context(‘/‘):
    print (url_for(‘foo_view‘))  #/foo
    print (url_for(‘bufar‘))  #/bar
    # url_for(‘bar_view‘) will raise werkzeug.routing.BuildError
    print (url_for(‘bar_view‘))  #端點bar_view是沒有定義的
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

程序運行結果
技術分享圖片

更多參考:

Flask中'endpoint'(端點)的理解