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