python项目使用jsonschema进行参数校验

python项目使用jsonschema进行参数校验

最近想要给一个新的openstack项目加上参数校验,过完年回来准备开工的时候,发现其他人已经在做了,对应的patch是:https://review.openstack.org/#/c/422547/

作者写的很棒,但是对比nova的实现还有一些不足,这里记一下学习笔记

参数校验这个功能,作者大致的实现思路很明确,通过装饰器进行,是这样

@check_input(参数)
def post():
    pass

def check_input(参数):

    def wrapper(f):
        ## check
        f()

    return wrapper

作者选用jscon schem进行参数校验,jsonschem的一个使用方式如下:

from jsonschema.validators import Draft4Validator
#这里的schem表示至少两个布尔变量
validator = Draft4Validator(
   schema={"items": {"type": "boolean"}, "minItems": 2},)
validator.validate([True, False])
validator.validate([True, True, True])

根据这个继续完善之前的代码

post_schem = {...}
validator = Draft4Validator(schem=post_schem)
@check_input(validator,request)
def post():
    pass

def check_input(参数):

    def wrapper(f):
        validator.validate(request.json)
        f()

    return wrapper

大概的逻辑是这样了,我们会有不同的参数,所以要把参数管理起来,所以作者写了一个单独的schemas.py 来管理所有schema

flavor_schema = {...}
jsonschema.Draft4Validator.check_schema(flavor_schema)
SCHEMAS = {'flavor_schema': flavor_schema}

作者为了更方便地使用validator,写了新的valiator

#validator.py
class Validator(object):
    def __init__(self, name):
        self.name = name
        self.schema = schemas.SCHEMAS.get(name)
        checker = jsonschema.FormatChecker()
        self.validator = validators.Draft4Validator(self.schema,
                                                format_checker=checker)

    def validate(self, data):
        try:
            self.validator.validate(data)
        except jsonschema.ValidationError as ex:
            LOG.exception(ex.message)
            # TODO(ramineni):raise valence specific exception
            raise Exception(ex.message)

最终的check_input函数实现:

##validator.py
def check_input(validator, request):
    def decorated(f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            data = request.json
            LOG.debug("validating input %s with %s", data, validator.name)
            validator.validate(data)
            ##这里看起来有个bug,应该是f(*args, **kwargs),未测试
            return f()
        return wrapper
    return decorated

这样通过下面的方式就可以进行参数校验了:

import validator
flavor_validator = validator.Validator('flavor_schema')
@validator.check_input(flavor_validator, request)

作者写的很好,但是个人觉得名叫validator的变量实在太多了,看的很糊涂。

看了下nova项目的validator实现,思路也是类似的,但是写的更漂亮了,使用起来也比这个更简单了,下面是nova中check_input函数的实现,区别在于不需要先构建validator再使用装饰器,validator在装饰器执行的过程中构建,代码更简洁优雅。另外使用kwargs['body']而不是request.json, 所以也不需要传入request。

def schema(request_body_schema, min_version=None, max_version=None):

    def add_validator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            #在_schema_validation_helper函数中构建了validator
            _schema_validation_helper(request_body_schema, kwargs['body'],
                                      min_version, max_version,
                                      args, kwargs)
            return func(*args, **kwargs)
        return wrapper

    return add_validator
posted on 2017-02-14 17:13  张宇飞  阅读(10689)  评论(0编辑  收藏  举报