簡體   English   中英

通過函數名稱找出燒瓶端點的正確方法是什么?

[英]what's the correct way to find out flask's endpoint by function name?

我有一個燒瓶演示,它需要訪問控制。 我的訪問控制基於函數名,我編寫了一個裝飾器來實現。

裝飾器的定義是:

def permission(fn):
    @wraps(fn)
    def wraped(*args, **kwargs):
        if _do_validate(fn.__name__):  # TODO do check user has privileges here
            return fn(*args, **kwargs)
        else:
            abort(401)
    return wraped

用法示例如下:

    @app.route('/index')
    @permission
    def index():
        return 'hello world'

如果用戶未指定端點,則此方法可以正常工作,因為Flask的默認端點為fn.__name__ ,在我的示例中為endpoint='index'

但是,當用戶指定端點或僅使用藍圖時,端點已更改。 例如:

    bl = Blueprint('admin', __name__)

    @bl.route('hello')
    @permission
    def hello():
        return 'hello world in blueprint'

端點更改為admin.hello

我不想在@permission指定任何arg,所以我寫了一個新的權限裝飾器,如下所示:

def permission(fn):
    @wraps(fn)
    def wraped(*args, **kwargs):
        m = fn.__module__.split('.')
        if len(m) <= 2:
            # app must define in the root module, so if user not use blueprint, juse use fn.__name__ as endpoint
            reg_f = fn.__name__
        else:
            # blue print must define in the submodule
            reg_f = '{0}.{1}'.format(m[1], fn.__name__)

        if _do_validate(ref_f):  # TODO do check user has privileges here
            return fn(*args, **kwargs)
        else:
            abort(401)
    return wraped

問題已解決,但我認為這並不優雅。

誰能給我更好的一個? 謝謝。

如果藍圖的名稱不等於模塊的__name__則您的解決方案無效。

我可以提出下一個解決方案:

from collections import defaultdict
registered = defaultdict(int)

def permission(fn):
    registered[fn.__name__] += 1
    fn.permission_token = "%s.%s" % (fn.__name__, registered[fn.__name__])

    @wraps(fn)
    def wraped(*args, **kwargs):
        if _do_validate(fn.permission_token):
            return fn(*args, **kwargs)
        else:
            abort(401)
    return wraped

@permission
def foo():
    pass

@permission
def bar():
    pass

def _do_validate(permission_token):
    return {foo.permission_token: True,
            bar.permission_token: False}.get(permission_token, False)

它的主要缺點是您必須在_do_validate的模塊中導入所有路由,才能創建此類“訪問控制列表”。

UPD:好的,由於您要使用端點值來檢查用戶訪問權限,因此下一個解決方案允許按視圖功能查找端點:

from collections import defaultdict
from flask import current_app

def find_endpoint(fn):
    # This loop can be optimized by maintaining
    # {permission_token => endpoint} dict.
    for endpoint, view_func in current_app.view_functions.items():
        if getattr(view_func, 'permission_token', None) == fn.permission_token:
            return endpoint
    return None

registered = defaultdict(int)

def permission(fn):
    registered[fn.__name__] += 1

    @wraps(fn)
    def wrapped(*args, **kwargs):
        endpoint = find_endpoint(wrapped)
        assert endpoint is not None
        if _do_validate(endpoint):
            return fn(*args, **kwargs)
        else:
            abort(401)

    # This will work because flask's route decorator returns
    # an original view function, not the wrapped one.
    wrapped.permission_token = "%s.%s" % (fn.__name__, registered[fn.__name__])
    return wrapped

def _do_validate(endpoint):
    # Or search in the DB
    return {'index': True,
            'bp.index': False}.get(endpoint, False)

但是,按端點檢查訪問權限並不是一個好主意,因為在開發過程中任何代碼更改都可能導致不希望的丟失(或更糟糕的是,正在獲取!)訪問權限。 因此,就目前而言,我認為對permission裝飾器使用額外的action (或resource )參數將是合理的。 使用此類參數,您可以自由修改端點/視圖功能,而不會產生任何不希望的副作用。

UPD2:實際上,在包裝函數中查找端點值的更簡單方法是使用request.endpoint

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM