Python装饰器 - func_wrapper()不带参数(给定1个)

时间:2015-07-01 04:32:09

标签: python flask decorator restful-authentication

我正在努力写下我的第一个decorator,但我有点失落。我希望decorator在执行main函数之前检查request来自特定地址。

目前我有:

def check_referrer(url):
    def func_wrapper():
        if request.referrer == url:
            return render_template('index.html', error=None)
        else:
            return render_template('login.html', error="some_error")
    return func_wrapper

@app.route('/index', methods = ['GET'])
@check_referrer("/venue/login")
def index():
    return

@ app.route / venue / login (此代码已简化)

@app.route('/venue/login', methods=['GET', 'POST'])
def login():

    error = None

    if login_valid():
        return redirect(url_for('index'))                                                                                   
    else:
        error = 'Invalid Credentials. Please try again.'

    return render_template('login.html', error=error)

1)我确信我正在做的事情有一些问题,但我首先需要理解为什么我会收到错误:

TypeError: func_wrapper() takes no arguments (1 given)

我以为我只是将argument传递给check_referrer

2)我的return陈述是否正确?

非常感谢任何帮助!

2 个答案:

答案 0 :(得分:1)

如果用户未登录,请考虑使用Flask-Login来处理身份验证和重定向。(或者仔细检查它是如何工作的。)它比你编写的函数更有效地处理它。

您编写的功能不是装饰器,还有许多其他问题。首先,它需要结构合理。它需要3层:

  1. 第1层函数获取url参数并生成装饰器。
  2. 第2层函数修饰一个函数并返回一个包装函数。
  3. 第3层是装饰视图。
  4. request.referrer包含整个网址,而不仅仅是与路由匹配的路径。使用urlparse获取路径。无法保证客户端的浏览器会发送引荐来源或正确的引荐来源,因此您不应该依赖此值。

    修饰函数需要接受可以传递给视图的任意参数。装饰函数应使用wraps正确包装原始函数。

    从同一视图中渲染不同的模板不是一个好主意。您应该重定向到相关视图,而不是呈现index.htmllogin.html。如果您需要将消息与重定向一起传递,请将它们放在session

    from functools import wraps
    from urllib.parse import urlparse
    # or from urlparse import urlparse for py2
    from flask import request, session, redirect
    
    def check_referrer(path):
        def decorator(f):
            @wraps(f)
            def decorated(*args, **kwargs):
                if not request.referrer:
                    session['error'] = 'no referrer'
                    return redirect('login')
    
                referrer_path = urlparse(request.referrer).path
    
                if referrer_path != path:
                    session['error'] = 'expected referrer {!r}'.format(path)
                    return redirect('login')
    
                 return f(*args, **kwargs)
    
            return decorated
    
        return decorator
    

答案 1 :(得分:0)

理解python装饰器的关键是:

@decorator
def func():
    pass

等同于:

func = decorator(func)

现在您可以理解为什么您的代码不起作用了:@check_referrer("/venue/login")返回一个不带参数的函数func_wrapper,因此func_wrapper不能成为装饰器。

您可以定义一个不带有2级内部函数参数的装饰器。要制作一个带参数的装饰器,你需要另一层内部函数,正如davidism的代码所示。

以下是发生的事情:

decorator = check_referrer(url) 
decorated = decorator(index) 
index = decorated