如何在运行时替换装饰器?

时间:2019-03-31 21:50:59

标签: python flask flask-httpauth

我正在尝试使another StackOverflow answer适应于有条件地应用装饰器以仅要求登录特定环境(最终是staging环境,但是需要development才能运行)。为此,我从以下内容开始

auth = HTTPDigestAuth()

def login_required(dec, condition):
    def decorator(func):
        if not condition:
            return func
        return dec(func)
    return decorator

@bp.route('/auth')
@login_required(auth.login_required, current_app.config['ENV'] != 'development')
def auth_route():
    return current_app.config['ENV']

启动服务器时,出现RuntimeError: Working outside of application context错误。在尝试了该问题早期版本的一些建议之后,我使RuntimeError消失了,但是在需要时仍然不能正确应用装饰器。这是当前版本:

def login_required(dec):
    def decorator(func):
        if not os.environ.get('ENV') != 'development':
            return func
        return dec(func)
    return decorator

@bp.route('/auth')
@login_required(auth.login_required)
def auth_route():
    return current_app.config['ENV']

这永远不会返回auth.login_reqired函数。它总是让浏览器无需身份验证就可以进入。

所以,我尝试将条件更改为

if not os.environ.get('ENV') is not None:

,然后显示身份验证。

是的,我已经在shell中完成了export ENV=development,并通过env命令进行了确认。但是即使那样,它仍无法像我期望的那样读取环境变量。

也许这仅仅是错误的方法吗?我的最终目标是要求在一个特定环境上进行身份验证。我走的路有可能吗?可能吗?

2 个答案:

答案 0 :(得分:1)

current_app是上下文本地代理,仅在请求期间 具有含义。这意味着您不能在请求之前 使用它,即作为装饰器的一部分。

使用current_app通常是一个好习惯,因为Flask允许配置多个应用程序。但是,在您的特定情况下,实际上没有必要。例如,下面的方法将起作用,因为它直接使用app对象而不是current_app代理:

from yourpackage import app

@bp.route('/auth')
@login_required(auth.login_required, app.config['ENV'] != 'development')
def auth():
    return current_app.config['ENV']

答案 1 :(得分:1)

让我从Flask的文档中粘贴一些东西

  

上下文的生命周期   将根据需要创建和销毁应用程序上下文。当Flask应用程序开始处理请求时,它将推送应用程序上下文和请求上下文。当请求结束时,它会弹出请求上下文,然后弹出应用程序上下文。通常,应用程序上下文将与请求具有相同的生存期。

现在让我们考虑装饰器的工作方式。它只是语法糖see this answer

因此,在加载模块时将调用login_required装饰器,并且由于当前应用未处理请求而无法使用。

我会这样做,将条件移动到装饰器函数(与您的示例有关)。在处理请求时将调用它,因此您应该可以访问current_app。