利用JSON-schema校验请求报文,封装转换错误信息,提示前台

简介: 利用json-schema-validate,对请求json做格式,数据合法性校验后,并将自定义提示内容返回给前台

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注解,编译器需要安装此插件。其余错误引入相关依赖即可

如果有什么好的想法,欢迎交流。






目录
相关文章
|
XML JSON API
淘宝商品详情API的调用流程(python请求示例以及json数据示例返回参考)
JSON数据示例:需要提供一个结构化的示例,展示商品详情可能包含的字段,如商品标题、价格、库存、描述、图片链接、卖家信息等。考虑到稳定性,示例应基于淘宝开放平台的标准响应格式。
|
JSON Java 数据格式
微服务——SpringBoot使用归纳——Spring Boot返回Json数据及数据封装——封装统一返回的数据结构
本文介绍了在Spring Boot中封装统一返回的数据结构的方法。通过定义一个泛型类`JsonResult&lt;T&gt;`,包含数据、状态码和提示信息三个属性,满足不同场景下的JSON返回需求。例如,无数据返回时可设置默认状态码&quot;0&quot;和消息&quot;操作成功!&quot;,有数据返回时也可自定义状态码和消息。同时,文章展示了如何在Controller中使用该结构,通过具体示例(如用户信息、列表和Map)说明其灵活性与便捷性。最后总结了Spring Boot中JSON数据返回的配置与实际项目中的应用技巧。
956 0
|
JSON 算法 vr&ar
目标检测笔记(五):查看通过COCOEvaluator生成的coco_instances_results.json文件的详细检测信息,包含AP、AR、MR和DR等
本文介绍了如何使用COCO评估器通过Detectron2库对目标检测模型进行性能评估,生成coco_instances_results.json文件,并利用pycocotools解析该文件以计算AP、AR、MR和DR等关键指标。
1309 1
目标检测笔记(五):查看通过COCOEvaluator生成的coco_instances_results.json文件的详细检测信息,包含AP、AR、MR和DR等
|
9月前
|
JSON Java 数据格式
Spring Boot返回Json数据及数据封装
在Spring Boot中,接口间及前后端的数据传输通常使用JSON格式。通过@RestController注解,可轻松实现Controller返回JSON数据。该注解是Spring Boot新增的组合注解,结合了@Controller和@ResponseBody的功能,默认将返回值转换为JSON格式。Spring Boot底层默认采用Jackson作为JSON解析框架,并通过spring-boot-starter-json依赖集成了相关库,包括jackson-databind、jackson-datatype-jdk8等常用模块,简化了开发者对依赖的手动管理。
819 3
|
XML JSON 安全
SSM:请求参数与回显&json
本文介绍了请求参数处理、过滤器和拦截器的使用方法。包括如何通过 `@RequestParam` 和 `@ModelAttribute` 绑定请求参数,使用 Lombok 简化实体类开发,实现过滤器处理字符编码,以及配置拦截器进行请求前后的处理。同时,还展示了如何使用 `@ResponseBody` 返回 JSON 数据,并解决了 JSON 编码问题。
245 0
|
JSON Java 数据格式
java操作http请求针对不同提交方式(application/json和application/x-www-form-urlencoded)
java操作http请求针对不同提交方式(application/json和application/x-www-form-urlencoded)
354 25
java操作http请求针对不同提交方式(application/json和application/x-www-form-urlencoded)
|
JSON API 数据格式
postman如何发送json请求其中file字段是一个图片
postman如何发送json请求其中file字段是一个图片
717 4
|
JSON JavaScript API
(API接口系列)商品详情数据封装接口json数据格式分析
在成长的路上,我们都是同行者。这篇关于商品详情API接口的文章,希望能帮助到您。期待与您继续分享更多API接口的知识,请记得关注Anzexi58哦!
|
JSON 前端开发 Java
【Spring】“请求“ 之传递 JSON 数据
【Spring】“请求“ 之传递 JSON 数据
356 2
|
JSON API 数据格式
使用Python发送包含复杂JSON结构的POST请求
使用Python发送包含复杂JSON结构的POST请求

热门文章

最新文章