利用JSON-schema校验请求报文,封装转换错误信息,提示前台
JSON-chema的语法就不讲述了,可自行查阅相关文档。
需求场景:前台请求接口的报文,为防止被非法拦截,需要后台再校验一遍报文合法性,之前都是在java代码中,用java代码来判断,查阅资料找到了json-schema-validate,一些基本简单的校验可以交给它来完成。
但是校验的具体哪个字段的提示信息不能以中文提示返回给前台,所以自己写了一个解析 校验结果的方法,来将检验结果错误信息以中文形式提示给前台。不多说,上代码
/*** * 读取定义的schema文件 @Slf4j public class ReadJsonFromFileUtil { /*** * 将文件中的json串解析成jsonNode * 现在未将结果缓存,会有效率问题 * @param fileName 文件名称 * @return public static JsonNode readJSONfile(String fileName) { //无法使用Cacheable 注解,JsonNode 未实现序列号接口。暂时用此方法 Object cacheValue = SingletonUtil.getInstance().get(BusinessConstant.SINGLETON_KEY_JSONNODE + fileName); if (cacheValue != null) { return (JsonNode) cacheValue; JsonNode instance = null; try { // in = ReadJsonFromFileUtil.class.getClassLoader().getResourceAsStream("json/" + fileName + ".json"); // instance = new JsonNodeReader().fromInputStream(in); instance = JsonLoader.fromResource("/json/" + fileName + ".json"); SingletonUtil.getInstance().set(BusinessConstant.SINGLETON_KEY_JSONNODE + fileName, instance); } catch (Exception e) { log.error("[ReadJsonFromFileUtil] readJSONfile Exception!!! \n ", e); return instance; }
我的jsonschema配置文件 放在了/src/main/resources/json目录下
@Slf4j public class BaseJsonValidateUtil { private final static JsonSchemaFactory factory = JsonSchemaFactory.byDefault(); /*** * 根据定义的json schema 校验json格式 * @param jsonFileName schema 文件名称 * @param params 传递的json 串 public static ProcessingReport ValidateJsonFormat(String jsonFileName, JSONObject params) { log.info("recevie validate schema is {}, \n request params is \n {} ", jsonFileName, params); JsonNode schema = ReadJsonFromFileUtil.readJSONfile(jsonFileName); JsonNode data = convertJsonToNode(params); Preconditions.checkNotNull(schema, "未定义数据模板"); Preconditions.checkNotNull(data, "缺少配置信息"); ProcessingReport report = factory.getValidator().validateUnchecked(schema, data); //如果定义了empty_message 或者error_message 的话,将这些信息以异常的方式抛给前端 convertMessage(report, schema); if (log.isDebugEnabled()) { log.debug("JsonSchema validate result {} \n", report); Preconditions.checkArgument(report.isSuccess(), "请求数据格式非法"); return report; /*** * 将json转换未 JsonNode * @param paramsJson * @return private static JsonNode convertJsonToNode(JSONObject paramsJson) { JsonNode actualObj = null; try { ObjectMapper mapper = new ObjectMapper(); actualObj = mapper.readTree(paramsJson.toJSONString()); } catch (IOException e) { log.error("convertJsonToNode Exception!!! \n {}", e); return actualObj; return actualObj; /*** *根据 report里面的错误字段,找到schema对应字段定义的中文提示,显示都前端 * @param report 校验json 的结果,里面包含错误字段,错误信息。 * @param schema 原始的schema文件。主要用来读取empty_message,error_message中文信息 private static void convertMessage(ProcessingReport report, JsonNode schema) { Iterator ProcessingMessage iter = report.iterator(); ProcessingMessage processingMessage = null; //保存校验失败字段的信息 JsonNode schemaErrorFieldJson = null; //原始校验返回的信息 JsonNode validateResult = null; while (iter.hasNext()) { processingMessage = iter.next(); validateResult = processingMessage.asJson(); //keyword表示 一定是不符合schema规范 JsonNode keywordNode = validateResult.get("keyword"); if (null != keywordNode) { //说明json validate 失败 String keyword = keywordNode.textValue(); schemaErrorFieldJson = findErrorField(schema, validateResult); //keyword 如果是require说明缺少必填字段,取schema中 字段对应的empty_message if ("required".equalsIgnoreCase(keyword)) { //如果是require,找到哪个字段缺少了 JsonNode missingNode = null; if (null == schemaErrorFieldJson) { missingNode = validateResult.get("missing"); schemaErrorFieldJson = schema.get("properties").get(missingNode.get(0).textValue()); if (null != schemaErrorFieldJson.get("empty_message")) { log.error("validate field 【requeied】[{}] fail.", JSON.toJSON(schemaErrorFieldJson)); Preconditions.checkArgument(false, schemaErrorFieldJson.get("empty_message").textValue()); } else { //非必填校验失败。说明是格式验证失败。取schema中 字段对应的error_message if (null != schemaErrorFieldJson.get("error_message")) { log.error("validate field 【validate】 [{}] fail.", JSON.toJSON(schemaErrorFieldJson)); Preconditions.checkArgument(false, schemaErrorFieldJson.get("error_message").textValue()); /*** * 根据校验结果的 schema pointer 中的url递归寻找JsonNode * @param schema * @param validateResult * @return private static JsonNode findErrorField(JsonNode schema, JsonNode validateResult) { //取到的数据是 String[] split = validateResult.get("schema").get("pointer").textValue().split("/"); JsonNode tempNode = null; if (!ArrayUtils.isEmpty(split)) { for (int i = 1; i split.length; i++) { if (i == 1) { tempNode = read(schema, validateResult, split[i]); } else { tempNode = read(tempNode, validateResult, split[i]); return tempNode; private static JsonNode read(JsonNode jsonNode, JsonNode validateResult, String fieldName) { return jsonNode.get(fieldName);
所以功能都在ValidateJsonFormat 方法中完成
第一步:读取指定的schema文件
JsonNode schema = ReadJsonFromFileUtil.readJSONfile(jsonFileName);
第二步:将请求的json转换成JsonNode
JsonNode data = convertJsonToNode(params);
第三步:调用json-schema校验方法,校验是否合法
ProcessingReport report = factory.getValidator().validateUnchecked(schema, data);
第四步:将校验返回的结果解析,并将定义的empty_message,或者error_message 提示给前台
PS:这两个字段是自定义字段。非json-schema标准语法字段。所以检验结果中,会提示语法错误,但不影响我们使用
empty_message: 表示没有此字段,触发了required校验,会取此字段信息提示到前台
error_message: 表示不符合定义的校验规则(enum,maxlength,minlength,pattern等等),会取此字段中的中文提示
如果这两个字段为定义,则会触发
Preconditions.checkArgument(report.isSuccess(), "请求数据格式非法");
schema样例
{ "type": "object", "description": "测试接口", "properties": { "productCode": { "type": "string", "minLength": 1 }, "birthday": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$" ,"empty_message":"请选择出生日期","error_message":"出生日期格式不正确"}, "gender": { "type": "string", "enum": ["M", "F"],"empty_message":"请选择性别","error_message":"性别输入非法" }, "period": { "type": "string", "enum": ["20", "30","999"],"empty_message":"请选择期间","error_message":"期间输入非法" }, "buyCount": { "type": "number", "multipleOf": 1,"minimum": 5,"maximum":10,"exclusiveMinimum":false,"exclusiveMaximum": false ,"empty_message":"请选择购买份数","error_message":"购买份数输入非法"}, "amount": { "type": "string","empty_message":"请输入金额"} "required": [ "productCode", "birthday", "gender", "period", "buyCount", "amount" }请求json样例
{ "productCode":"TEST", "birthday":"2010-02-09", "gender": "M", "period": "20", "buyCount":5, "amount":"50000" }
controller
@RequestMapping(value = "/check", method = RequestMethod.POST) @ResponseBody public JSONObject check(@RequestBody JSONObject params) { JSONObject result = BusinessCommonResponseUtil.createSuc(null); try { ProcessingReport schemaValidateReport = BaseJsonValidateUtil.ValidateJsonFormat("schema_rial", params); log.info("response ={}", result); return result; } catch (IllegalArgumentException argException) { log.error("check IllegalArgumentException!!! \n ", argException); return BusinessCommonResponseUtil.createFail(argException.getMessage()); } catch (Exception e) { log.error(check Exception!!! \n ", e); return BusinessCommonResponseUtil.createFail("系统异常"); }controller返回的json报文格式根据业务而定
convertMessage方法说明: 此方法无法将 type: array类型的元素的错误信息以中文提示给前台。
@Slf4j 注解属于是lombok注解,编译器需要安装此插件。其余错误引入相关依赖即可
如果有什么好的想法,欢迎交流。
webapi 控制json的字段(key)显示顺序 原文:webapi 控制json的字段(key)显示顺序 使用两个c#的特性: 加在类上的:[DataContract] 加在字段上的:[DataMember(Name = ResultCode ,EmitDefaultValue = true,IsRequired = true, Order...
相关文章
- Qt之图形(转换)
- 【数据结构与算法】栈结构及其应用匹配括号,进制转换,中序转后序,计算后续
- Python标准库:codecs【专门做编码转换】【open()、encode()、decode()、lookup()、getencoder()】【读写文件codecs.open()优于open()】
- 将扁平化的JSON属性转换为嵌套的JSON
- BMP图片转换为JPEG图片
- linux 字符串编码转换
- C printf、scanf 和格式转换(控制、占位)符
- 【Rust】Option-转换
- iconv 文件编码转换
- javascript 转换大小写字母
- UIColor和 同 CIColor 与 CGColor 之间的联系、转换
- Python dateutil.parse 日期转换库安装使用方法
- 目标检测yolo格式与labelme标注互相转换及其可视化
- Java 基础类型转换byte数组, byte数组转换基础类型