Flask:如何为所有方法注册一个包装器

Flask: how to register a wrapper to all methods

我一直在从瓶子换到烧瓶。如果我需要的代码不超过 20 行,我是那种更喜欢自己编写代码而不是从 Internet 下载程序包的人。以支持 Basic 身份验证协议为例。在瓶子里我可以写:

def allow_anonymous():
    """assign a _allow_anonymous flag to functions not requiring authentication"""
    def wrapper(fn):
        fn._allow_anonymous = True
        return fn
    return wrapper


def auth_middleware(fn):
    """perform authentication (pre-req)"""
    def wrapper(*a, **ka):
        # if the allow_anonymous annotation is set then bypass this auth
        if hasattr(fn, '_allow_anonymous') and fn._allow_anonymous:
            return fn(*a, **ka)
        user, password = request.auth or (None, None)
        if user is None or not check(user, password):
            err = HTTPError(401, text)
            err.add_header('WWW-Authenticate', 'Basic realm="%s"' % realm)
            return err
        return fn(*a, **ka)
    return wrapper

...

app = Bottle()
app.install(middleware.auth_middleware)

以上代码为所有方法提供了对基本身份验证协议的完全支持,除非用 @allow_anonymous 包装器明确修饰。我只是烧瓶的初学者。我很难在不添加对更多 python 包或过多样板的依赖的情况下在烧瓶中完成上面的瓶兼容代码。 flask 中是如何直接清楚地处理的?

如果你愿意,你绝对可以自己使用 flask-httpauth 的一些功能:-P

我想你需要玩一些 before_request 游戏(不是很漂亮),或者用每个 api 端点的修饰方法调用 flask 的 add_url_rule (或者有您自己的 route 装饰器将执行此操作)。 add_url_rule 获取一个视图函数,它通常是您的 api 端点处理程序,但在您的情况下,将是一个包装方法,其方式与您在 post 中提供的方法非常相似( auth_middleware).

要点:

from flask import Flask, make_response, request

app = Flask(__name__)

def view_wrapper(fn):
    """
    Create a wrapped view function that checks user authorization
    """
    def protected_view(*a, **ka):
        # if the allow_anonymous annotation is set then bypass this auth
        if hasattr(fn, '_allow_anonymous') and fn._allow_anonymous:
            return fn(*a, **ka)
        # consult werkzeug's authorization mixin
        user, password = (request.authorization.username, request.authorization.password) if request.authorization else (None, None)
        if user is None or not check(user, password):
            err_response = make_response(text, 401)
            err_response.headers['WWW-Authenticate'] = 'Basic realm="%s"' % realm
            return err_response
        return fn(*a, **ka)

    return protected_view


# An endpoint
def hello():
    return 'hello there'

app.add_url_rule('/', 'hello', view_wrapper(hello))

当然,这可以(并且应该)通过 Blueprints 等进一步增强

注意 #1:这是从 SO 中的单独答案中抄袭的。

注意#2:这不使用蓝图。同样,我是 Flask 的新手,我很欣赏蓝图将有助于应用程序扩展,但一次一步......

def allow_anonymous(decorated_function):
    decorated_function.is_public = True
    return decorated_function

@app.before_request
def auth_middleware():
    fn = app.view_functions[request.endpoint]
    if hasattr(fn, 'allow_anonymous') and fn.allow_anonymous:
        # anonymous permitted
        return
    elif my_custom_authentication():
        # anonymous not permitted authentication succeeded
        return
    else:
        # anonymous not permitted authentication failed
        err_response = make_response(text, 401)
        err_response.headers['WWW-Authenticate'] = 'Basic realm="%s"' % realm
        return err_response

@app.route('/public_thing')
@allow_anonymous
def public_thing():
   return 'yo'

@app.route('/regular_thing')
def regular_thing():
   return 'if you can read this youre authenticated.'