Поскольку декоратор @route
должен зарегистрировать представление с текущим обратным вызовом, переданным декоратору, он должен быть внешним декоратором , чтобы получить правильную функцию, которая будет вызываться при обработке запроса.
Это создает возможную ситуацию, когда представление было оформлено, но поскольку декораторы расположены в неправильном порядке, функция декорирования не вызывается.Если используется для украшения представлений, которые требуют, чтобы пользователь вошел в систему, имел определенную роль или имел определенный флаг, проверка будет молча исключена.
Наше текущее исправление состоит в том, чтобы стандартным действием было отказатьдоступ к ресурсу, затем требуется декоратор, чтобы разрешить доступ.В этом случае, если декоратор не вызывается во время обработки запроса, запрос завершится неудачей.
Но есть случаи использования, когда это становится громоздким, поскольку требует декорирования всех представлений, кромете немногие, которые должны быть освобождены.Для чистой иерархической структуры это может работать, но для проверки отдельных флагов структура может усложниться.
Есть ли правильный способ обнаружить, что нас вызывают в полезном месте в иерархии декораций?Т.е. можем ли мы обнаружить, что еще не был route
декоратор, примененный к функции, которую мы получаем для переноса?
# wrapped in wrong order - @require_administrator should be after @app.route
@require_administrator
@app.route('/users', methods=['GET'])
Реализован как:
def require_administrator(func):
@functools.wraps(func)
def has_administrator(*args, **kwargs):
if not getattr(g, 'user') or not g.user.is_administrator:
abort(403)
return func(*args, **kwargs)
return has_administrator
Здесь я хотел бы определить, переносится ли мой пользовательский декоратор после @app.route
, и, следовательно, никогда не будет вызываться при запросе
Использование functools.wraps
заменяет упакованную функцию новой во всех отношениях, поэтому просмотр __name__
функции, которая будет упакована, не удастся.Это также происходит на каждом этапе процесса обёртывания декоратора.
Я пробовал смотреть как traceback
, так и inspect
, но не нашел приличного способа определения правильности последовательности.
Обновление
Моим лучшим на данный момент лучшим решением является проверка имени вызываемой функции по набору зарегистрированных конечных точек.Но так как Route()
декоратор может изменить имя конечной точки, мне придется поддерживать это и для моего декоратора в этом случае, и он будет молча проходить, если другая функция использовала то же имя конечной точки, что и текущаяfunction.
Также необходимо выполнить итерацию набора зарегистрированных конечных точек, поскольку я не смог найти простой способ проверить, существует ли только имя конечной точки (возможно, более эффективный, если попытаться создать URL с ним ипоймать исключение).
def require_administrator_checked(func):
for rule in app.url_map.iter_rules():
if func.__name__ == rule.endpoint:
raise DecoratorOrderError(f"Wrapped endpoint '{rule.endpoint}' has already been registered - wrong order of decorators?")
# as above ..