Python 装饰器 - func_wrapper() 不接受任何参数(给定 1 个)

Python decorator - func_wrapper() takes no arguments (1 given)

我正在尝试写我的第一个 decorator 但有点迷茫。我希望这个 decorator 在执行主函数之前检查 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 陈述正确吗?

如有任何帮助,我们将不胜感激!

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


你写的函数不是装饰器,还有很多其他问题。首先,它需要适当的结构。它需要 3 层:

  1. 第 1 层函数采用 url 参数并生成装饰器。
  2. 第 2 层函数装饰一个函数,returns一个包装函数。
  3. 第三层为装饰视图

request.referrer 包含整个 url,而不仅仅是匹配路由的路径。使用 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

理解python装饰器的关键是:

@decorator
def func():
    pass

等同于:

func = decorator(func)

现在你可以理解为什么你的代码不起作用了:@check_referrer("/venue/login") returns 一个没有参数的函数 func_wrapper,所以 func_wrapper 不能是装饰器。

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

事情是这样的:

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