Flask app.add_url_rule 装饰器错误

1 投票
1 回答
1900 浏览
提问于 2025-04-18 02:42

我在Flask的路由中有一堆装饰器,想把它们合并成一个(包括 @app.route)。

我有一个这样的 @route 函数:

from functools import wraps
def route(route, method):
    def decorator(f):
        print 'decorator defined'
        print 'defining route'
        app.add_url_rule(route, methods=method, view_func=f)
        print 'route defined'
        @wraps(f)
        def wrapper(*args, **kwargs):
            print 'Hello'
            # do stuff here such as authenticate, authorise, check request json/arguments etc.
            # these will get passed along with the route and method arguments above.
            return f(*args, **kwargs)
        return wrapper
    return decorator

还有一个示例路由:

@route('/status', ['GET'])
def status():
    return Response('hi', content_type='text/plain')

这个路由是定义好了,但 wrapper() 从来没有被调用,这真是奇怪。当我把 app.add_url_rule 移到装饰器外面,放到文件的最后时,wrapper() 就会被调用;所以在Flask启动时会打印 decorator defined,而当我访问 GET /status 路由时,Hello 也会如预期那样打印出来。

但是,当我把 app.add_url_rule 放回到上面的装饰器里时,decorator defined 在启动时会打印,但当我调用 GET /status 时却没有打印 Hello,就好像 app.add_url_rule 以某种方式覆盖了我定义的 wrapper() 函数。

这是为什么呢?看起来 app.add_url_route 以一种奇怪或意想不到的方式劫持了我的函数。

我该怎么做才能在访问路由时调用 wrapper(),同时又能在装饰器中定义 app.add_url_rule 呢?

1 个回答

2

你注册的是原始函数,而不是包装函数,给Flask用的。当路由匹配时,Flask会调用f,而不是wrapper,因为你注册的是这个原始函数。

要让Flask在路由匹配时调用wrapper,你需要这样做:

def route(route, method):
    def decorator(f):
        print 'decorator defined'
        print 'defining route'
        @wraps(f)
        def wrapper(*args, **kwargs):
            print 'Hello'
            # do stuff here such as authenticate, authorise, check request json/arguments etc.
            # these will get passed along with the route and method arguments above.
            return f(*args, **kwargs)

        app.add_url_rule(route, methods=method, view_func=wrapper)
        print 'route defined'
        return wrapper
    return decorator

撰写回答