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