JSON Schema Validation介绍

工作中经常会开发一些接口公布出去,接口以HTTPHandler方式挂载在ASP.NET上,是以*.filetype的后缀形式路由到具体的处理类中。接口参数以JSON格式通过post方法写在Request body中,宿主的ERP系统并没有提供对应的参数校验框架,因此无法像其他基于其他API框架的形式,对输入参数进行统一的校验。

本文针对这种情况,将介绍下JSON.NET自带的JSON Schema校验方式。

之前在API实现中,对于多个参数的校验,往往是一并验证其合法性,否则只能依次校验单个参数,这样代码会显得很冗长。

如,若要对每个参数都进行校验,其方法实现大体如下:

 
void DoSomething(object parma1, object parma2, object parma3)
{
    if(param1 is invalid) throw exeception(param1 is invalid)
    if(param2 is invalid) throw exeception(param2 is invalid)
    if(param3 is invalid) throw exeception(param3 is invalid)
    
    //do something
    
}

上述参数的合法性校验的代码块,不利于代码的阅读,也会使得这些非业务的处理方法充斥在整个方法中,干扰了关键的业务。因此,对于一些关键参数却无对应错误业务场景的参数,我都放在一起来校验:

如:

void DoSomething(object parma1, object parma2, object parma3)
{
    if((param1 is invalid) or (parma2 is invalid) or (parma3 is invalid)
        throw exeception(params has invalid)

    //do something

}

这样从代码长度来看是简洁了,可读性高了,可却给API使用者带来了困惑,对于多个参数来说,这里无法返回具体是哪个参数非法,给使用者带来了差错负担。因为平时很多API都是公布给部门多个同事使用,虽然API文档中有参数示例,但因为对于系统不是很熟悉,总免不了要来询问,每次都要一一解答,查看参数,个人觉得不胜其烦。于是想看看如何实现更友好、简洁的统一校验参数。

JSON Validation

由于API方法的参数都来自于HTTP请求中的Body,body是JSON格式的字符串,因此自然想到从JSON入手。项目中用的是JSON.NET,通过其官网文档发现有JSON validation这个功能,于是花了一上午的时间大致看了下各个文档,有了一定的了解。下面通过一个简单的例子来介绍下JSON Validation的使用方式,假设我们通过JSON串来表示产品目录,每个产品包含以下几个属性:

* productId:产品标识
* productName:产品名称
* price:  售价
* tags: 可选的标签

那么我们可能会得到以下的JSON串:

{
    "productId": 1,
    "productName": "A green door",
    "price": 12.50,
    "tags":["home", "green"]
}

JSON Scheme

JSON.NET Schema是用来校验任何JSON的合法性,它可以对已存在的JSON串进行校验或者用来校验你读取或写入的JSON格式。具体的介绍参考 JSON Schema

下面来看一下Schema的结构:

{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$id": "http://example.com/product.schema.json",
    "title": "Product",
    "description": "A product in the catalog",
    "type": "object"
}

各属性含义如下:

* Schema Keyword: $shema和$id
* Scheme Annotations: title和description
* Validation Keyword: type

实际上Scheme Keyword和Schema Annotation在一般情况下都是可以忽略的,我们要关注的是Validation Keyword和下面要介绍的properties.

根据产品的特性,productId可以是一个唯一的数字值,且不能为空。

因此我们在前面的JSON Schema中添加一下特性:

* properties: 这个是validation keyword的关键字,用于表述对具体元素的校验规则;
* productId:假设为整形,且不能为空;
* required:validation关键字,是一个数组,表示数组内的元素不能缺省。

加入之后的JSON Schema如下:

 
{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$id": "http://example.com/product.schema.json",
    "title":"Product",
    "description":"A product catalog",
    "type":"object",
    "properties": {
        "productId":{
            "description":"The unique identifier form a product",
            "type":"integer"
        }
    },
    "required":["productId"]
}

对于productName字段,type应该为string, 且不能为空,因此也是应该在required数组里面。

下面来谈谈price, type当然不能是integer了,因为正常都是包含小数的,所以应该是数值型number即可。而天下没有免费的午餐,所以我们可以添加规定值必须大于0.

将上述两个字段添加后的Schema如下:

{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$id": "http://example.com/product.schema.json",
    "title": "Product",
    "description": "A product from Acme's catalog",
    "type": "object",
    "properties": {
        "productId": {
        "description": "The unique identifier for a product",
        "type": "integer"
        },
        "productName": {
        "description": "Name of the product",
        "type": "string"
        },
        "price": {
        "description": "The price of the product",
        "type": "number",
        "exclusiveMinimum": 0
        }
    },
    "required": [ "productId", "productName", "price" ]
}

这里的exclusiveMinium:0,表示输入的值必须大于0. 如果对于exclusiveMinium关键词属性不清晰,可以参见JSON Schema的说明文档: json-schema-validation

接下来再看看tags属性,一个商品可以有很多标签,但标签之间应该不能重复。所以tags的校验应该是一个数组,且数组元素之间是唯一的。

只对tags属性来看:

"tags": {
        "description": "Tags for the product",
        "type": "array",
        "items": {
            "type": "string"
        },
        "minItems": 1,
        "uniqueItems": true
    }

可以看到这里type为array表示一个数组,items是对数组里面的每个元素而言,minItems和uniqueItems则其义自见了。

嵌套的Schema

有时候我们的JSON串可能是嵌套的,如产品有尺寸的属性,用长、宽、高来表示。这时尺寸是product的元素,而尺寸本身也是一个对象,与product拥有相同的JSON validation结构。在我们的Validation中,只需要将dimensions的type指定为object,即可以用properties来校验其子元素。

将上述部分全部整合后的JSON Schema validation如下:

{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$id": "http://example.com/product.schema.json",
    "title": "Product",
    "description": "A product from Acme's catalog",
    "type": "object",
    "properties": {
        "productId": {
        "description": "The unique identifier for a product",
        "type": "integer"
        },
        "productName": {
        "description": "Name of the product",
        "type": "string"
        },
        "price": {
        "description": "The price of the product",
        "type": "number",
        "exclusiveMinimum": 0
        },
        "tags": {
        "description": "Tags for the product",
        "type": "array",
        "items": {
            "type": "string"
        },
        "minItems": 1,
        "uniqueItems": true
        },
        "dimensions": {
        "type": "object",
        "properties": {
            "length": {
            "type": "number"
            },
            "width": {
            "type": "number"
            },
            "height": {
            "type": "number"
            }
        },
        "required": [ "length", "width", "height" ]
        }
    },
    "required": [ "productId", "productName", "price" ]
    }

最后,我们给出一个符合上述validation的JSON串:

{
    "productId": 1,
    "productName": "An ice sculpture",
    "price": 12.50,
    "tags": [ "cold", "ice" ],
    "dimensions": {
    "length": 7.0,
    "width": 12.0,
    "height": 9.5
    }
}

JSON.NET提供了在线校验的工具,可以根据你定义的Schema Validation实时反馈输入的JSON串是否合法。对于日常开发时,可以先根据参数将对应的Schema定义好,这样可以节省不少调试的时间。Online Validation Tool.

Reference:

JSON.NET

JSON Schema getting start

JSON Schema validation

Online Validation tool

标签: json, JSON.NET, JSON Schema, Validation, API

添加新评论