工作中经常会开发一些接口公布出去,接口以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: