将两个Python装饰器合并为一个

5

这里有两个装饰器,我希望将它们合并,因为它们非常相似,唯一的区别是如何处理未经身份验证的用户。我希望有一个单独的装饰器,可以调用一个参数。

# Authentication decorator for routes
# Will redirect to the login page if not authenticated
def requireAuthentication(fn):
    def decorator(**kwargs):
        # Is user logged on?
        if "user" in request.session:
            return fn(**kwargs)
        # No, redirect to login page
        else:
            redirect('/login?url={0}{1}'.format(request.path, ("?" + request.query_string if request.query_string else '')))
    return decorator

# Authentication decorator for routes
# Will return an error message (in JSON) if not authenticated
def requireAuthenticationJSON(fn):
    def decorator(**kwargs):
        # Is user logged on?
        if "user" in request.session:
            return fn(**kwargs)
        # No, return error
        else:
            return {
                "exception": "NotAuthorized",
                "error" : "You are not authorized, please log on"
            }
    return decorator

目前我正在使用这些装饰器来处理特定的路由,例如:

@get('/day/')
@helpers.requireAuthentication
def day():
    ...

@get('/night/')
@helpers.requireAuthenticationJSON
def night():
    ...

我更喜欢这个选项:

@get('/day/')
@helpers.requireAuthentication()
def day():
    ...

@get('/night/')
@helpers.requireAuthentication(json = True)
def night():
    ...

我正在使用Bottle框架的Python 3.3版本。我想知道是否可以做到我想要的,该如何实现?

2个回答

2
只需添加另一个包装器来捕获json参数:
def requireAuthentication(json=False):
    def decorator(fn):
        def wrapper(**kwargs):
            # Is user logged on?
            if "user" in request.session:
                return fn(**kwargs)

            # No, return error
            if json:
                return {
                    "exception": "NotAuthorized",
                    "error" : "You are not authorized, please log on"
                }
            redirect('/login?url={0}{1}'.format(request.path, ("?" + request.query_string if request.query_string else '')))
        return wrapper
    return decorator

我已经将您原来的requireAuthentication函数重命名为decorator(因为这就是该函数的作用,它装饰了fn),并将旧的decorator重命名为wrapper,这是通常的惯例。
无论您在@之后放什么都是一个表达式,首先进行求值以找到实际的装饰器函数。 @helpers.requireAuthentication()表示您要调用requireAuthentication,然后它的返回值被用作@行应用于的函数的实际装饰器。

1
你可以为这两个装饰器创建包装器:
def requireAuthentication(json=False):
    if json:
        return helpers.requireAuthenticationJSON
    else:
        return helpers.requireAuthentication

或者

import functools
# Authentication decorator for routes
# Will redirect to the login page if not authenticated
def requireAuthentication(json=False):
    def requireAuthentication(fn):
        @functools.wraps(fn)
        def decorator(*args, **kwargs):
            # Is user logged on?
            if "user" in request.session:
                return fn(*args, **kwargs)
            if json:
                 return {
                "exception": "NotAuthorized",
                "error" : "You are not authorized, please log on"
            }
            return redirect('/login?url={0}{1}'.format(request.path, 
                                                       ("?" + request.query_string if request.query_string else '')))
        return decorator
    return requireAuthentication

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接