【问题标题】:Flask: Decorator to verify JSON and JSON SchemaFlask:验证 JSON 和 JSON Schema 的装饰器
【发布时间】:2014-08-05 23:39:57
【问题描述】:

我有一个烧瓶应用程序,其调用需要 JSON 有效负载。在处理每个调用之前,我有一个两步错误检查过程:

  • 断言负载是有效的 JSON
  • 断言 JSON 负载符合特定架构

以下列方式实现:

@app.route('/activate', methods=['POST'])
def activate():
    request_id = request.__hash__()

    # Assert that the payload is a valid JSON
    try:
        input = request.json
    except BadRequest, e:
        msg = "payload must be a valid json"
        return jsonify({"error": msg}), 400

    # JSON Schema Validation
    try:
        validate(request.json, app.config['activate_schema'])
    except ValidationError, e:
        return jsonify({"error": e.message}), 400

由于这段代码在多次调用中重复,我想知道我是否可以优雅地将其移动到装饰器中,形式为:

@validate_json
@validate_schema(schema=app.config['activate_schema'])
@app.route('/activate', methods=['POST'])
def activate():
    ....

问题在于request 参数是隐式的:我可以在函数中引用它,但它不是它的参数。因此,我不确定如何在装饰器中使用它。

如何使用 Python 装饰器实现验证检查?

【问题讨论】:

  • 我有点希望有一个图书馆可以做到这一点。 @Martijn 的回答非常简单,但有人知道吗?

标签: python flask decorator jsonschema python-decorators


【解决方案1】:

只需在您的装饰器中使用request 全局上下文。它在任何请求期间都可用

from functools import wraps
from flask import (
    current_app,
    jsonify,
    request,
)


def validate_json(f):
    @wraps(f)
    def wrapper(*args, **kw):
        try:
            request.json
        except BadRequest, e:
            msg = "payload must be a valid json"
            return jsonify({"error": msg}), 400
        return f(*args, **kw)
    return wrapper


def validate_schema(schema_name):
    def decorator(f):
        @wraps(f)
        def wrapper(*args, **kw):
            try:
                validate(request.json, current_app.config[schema_name])
            except ValidationError, e:
                return jsonify({"error": e.message}), 400
            return f(*args, **kw)
        return wrapper
    return decorator

在应用@route 装饰器之前应用这些装饰器;你想注册包装的函数,而不是路由的原始函数:

@app.route('/activate', methods=['POST'])
@validate_json
@validate_schema('activate_schema')
def activate():
    input = request.json

【讨论】:

  • 装饰器的顺序重要吗? @app.route 装饰器应该先出现吗?
  • @AdamMatan: @route() 注册可调用对象,它应该放在最前面以应用于装饰视图函数。我将添加它以及实现。
  • 不应该把@validate_json放在装饰器列表的最底部吗?
  • @StefanSeemayer:不,从上到下是装饰器从外到内的顺序。所以activate()validate_schema() 调用,而validate_json() 又被调用;后者由@app.route() 注册为视图处理程序。
  • @StefanSeemayer:所以当对注册路由的请求进入时,Flask 调用validate_json(),后者调用validate_schema(),后者调用activate() 函数。
【解决方案2】:

现在你可以直接使用@expect_json

举例

from flask import Flask, jsonify, g, url_for
from flask_expects_json import expects_json
# example imports
from models import User
from orm import NotUniqueError

app = Flask(__name__)

schema = {
    'type': 'object',
    'properties': {
        'name': {'type': 'string'},
        'email': {'type': 'string'},
        'password': {'type': 'string'}
    },
    'required': ['email', 'password']
}


@app.route('/register', methods=['POST'])
@expects_json(schema)
def register():
    # if payload is invalid, request will be aborted with error code 400
    # if payload is valid it is stored in g.data

    # do something with your data
    user = User().from_dict(g.data)
    try:
        user.save()
    except NotUniqueError as e:
        # exception path: duplicate database entry
        return jsonify(dict(message=e.message)), 409

    # happy path: json response
    resp = jsonify(dict(auth_token=user.encode_auth_token(), user=user.to_dict()})
    resp.headers['Location'] = url_for('users.get_user', user_id=user.id)
    return resp, 201

from flask import Flask
from flask_expects_json import expects_json


app = Flask(__name__)


schema = {
    'type': 'object',
    'properties': {
        'name': {'type': 'string',  "minLength": 4, "maxLength": 15},
        'mobile': {'type': 'string', "pattern": "^[1-9]{1}[0-9]{9}$"},
        'email': {'type': 'string', "pattern": "[^@]+@[^@]+\.[^@]"},
        'password': {'type': 'string', "pattern": "^.*(?=.{8,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&+=]).*$"}
    },
    'required': ['name', 'mobile', 'email', 'password']
}


@app.route('/', methods=['POST'])
@expects_json(schema)
def index():
    values = request.get_json()
    print(values)
    return values

here获取更多信息

【讨论】:

    【解决方案3】:

    一个迟到的答案,但您可能正在寻找类似棉花糖(flask-marshmallow)或烤棉花糖之类的东西。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-14
      • 1970-01-01
      • 2021-04-18
      • 1970-01-01
      • 1970-01-01
      • 2019-10-18
      • 2015-06-18
      • 1970-01-01
      相关资源
      最近更新 更多