@ParamsValidate系列教程 https://blog.csdn.net/u010606397/article/category/7689866
一、ValidateInterface接口
在《@ParamsValidate 一 入门》教程中,我们新增了一个类ValidateInterfaceImpl
@Component
public class ValidateInterfaceImpl extends ValidateInterfaceAdapter{
}
这个类没有覆盖父类的任何方法,使用了默认的配置。当需要自定义一些配置信息时,如:
1、修改json文件的根目录
2、修改读取json文件的解析器
3、修改校验级别
4、修改未通过校验时返回给前端的信息
5、使用redis存储json文件
就需要覆盖ValidateInterfaceAdapter的方法。
在params-validate的源码中,ValidateInterfaceAdapter是一个适配器,它提供了对ValidateInterface接口的默认实现。
ValidateInterface、ValidateInterfaceAdapter、ValidateInterfaceImpl 的关系如下图:
ValidateInterface接口共有6个方法(1.8-RELEASE版本),如下:
public interface ValidateInterface {
String basePath();
String getLevel();
Parser getParser();
Object validateNotPass(ResultValidate resultValidate);
Map<String, Object> getKeyCache(ValidateConfig validateConfig);
void setFileCache(ValidateConfig validateConfig, Map<String, Map<String, Object>> json);
}
ValidateInterfaceAdapter提供对接口的默认实现:
/**
* 默认根目录是resources/validate,json文件都放在此目录中
* 可在ValidateInterfaceAdapter子类覆盖此方法,改变默认根目录
*/
@Override
public String basePath() {
return "validate/";
}
/**
* 校验级别
* PvConst.LEVEL_STRICT 严格模式,发生异常,校验不通过,默认
* PvConst.LEVEL_LOOSE 宽松模式,发生异常,不校验
*
* params-validate读取json文件或者json文件编写不合法等导致params-validate发生异常,默认是会拦截请求,不执行controller方法
* 若在ValidateInterfaceAdapter子类覆盖此方法,返回PvConst.LEVEL_LOOSE,当params-validate校验发生异常,就不校验了,放行请求,执行controller方法。
* 注意:当level设置为PvConst.LEVEL_LOOSE,在params-validate代码未发生异常的前提下,请求参数不符合校验规则,不会放行请求,params-validate返回校验信息给前端,controller方法不会执行。
*/
@Override
public String getLevel(){
return PvConst.LEVEL_STRICT;
}
/**
* json解析器
* 1、使用默认解析器jackson,返回null
* 2、使用gson,请返回 new Parser(Gson.class)。需要引入gson依赖
* 3、使用fastjson,请返回new Parser(JSON.class, Feature[].class)。需要引入fastjson依赖
* 提供对gson、fastjson的支持是因为jackson不支持在json文件中写注释。为了支持fastjson,搞得好坑爹。
*/
@Override
public Parser getParser() {
return null;
}
自定义返回信息
当请求参数未通过校验,若要自定义返回信息,可覆盖validateNotPass(ResultValidate resultValidate)方法
看下图:
未通过校验的参数信息放在了msgList中,name是请求参数的key,其他的是校验规则。可以从msgList中拿数据,组装后返回给前端。
注意:若请求参数全部填写正确,validateNotPass(ResultValidate resultValidate)不会执行,而是执行controller中的方法。
使用redis储存json文件
params-validate需要从json文件中读取校验规则,若每次校验都要读取文件,性能开销大,可在首次读取json文件后,就把json文件的内容存储到redis中,之后都从redis中读取校验规则。
覆盖getKeyCache、setFileCache这两个方法,即可实现params-validate与redis的整合。示例代码如下:
/**
* ValidateConfig这个对象储存@ParamsValidate注解的值
* 获取@ParamsValidate中的key在redis中的校验规则
*/
@Override
public Map<String, Object> getKeyCache(ValidateConfig validateConfig) {
String redisKey = createRedisKey(validateConfig);
return (Map<String, Object>)redisTemplate.opsForHash().get(redisKey, validateConfig.getKey());
}
/**
* 将整个json文件的内容存储为hash结构
*/
@Override
public void setFileCache(ValidateConfig validateConfig, Map<String, Map<String, Object>> json) {
String redisKey = createRedisKey(validateConfig);
redisTemplate.opsForHash().putAll(redisKey, json);
}
/**
* 使用basePath()返回的目录名+@ParamsValidate的file文件名作为redis的key
*/
private String createRedisKey(ValidateConfig validateConfig){
String basePath = PvUtil.trimBeginEndChar(basePath(), '/') + "/";
String fileName = validateConfig.getFile().substring(0, validateConfig.getFile().lastIndexOf(".json"));
fileName = PvUtil.trimBeginEndChar(fileName, '/');
String temp = basePath + fileName;
return temp.replaceAll("[\\/\\-]",":");
}
举例说明,resources/validate/learn-params-validate.json的内容如下:
{
"userValidate": {
"name": {
"request": true,
"minLength": 10,
"maxLength": 100,
"regex": "^[a-zA-Z0-9_]+$",
"message": "名字必须是字母、数字、_"
},
限于篇幅,省略部分内容
},
"userValidate02": {
限于篇幅,省略部分内容。
}
}
params-validate首次读取此文件,在redis中生成的数据如下:
至此,已经可以使用缓存了,但是有个bug,即修改某些校验规则后,项目重新上线,我们要删除缓存中的校验规则,不然就会出现脏读取。下面在ValidateInterfaceImpl的基础上给出一种简单实现。实际上新建一个类实现ApplicationRunner,在run方法中删除redis中的校验规则才是最好的方案。
@Component
public class ValidateInterfaceImpl extends ValidateInterfaceAdapter implements InitializingBean {
@Autowired
RedisTemplate redisTemplate;
//类属性初始化后,删除redis缓存校验规则
@Override
public void afterPropertiesSet() throws Exception {
ExecutorService es = Executors.newFixedThreadPool(1);
es.execute(new Runnable() {
@Override
public void run() {
Set<String> keys = redisTemplate.keys(basePath().replace("/",":") + "*");
redisTemplate.delete(keys);
}
});
es.shutdown();
}
}
二、@ParamsValidate详解
1、file的配置:
ValidateInterface#basePath()+file值,就是json文件的路径。假设ValidateInterface#basePath()返回值是"validate/"
①、若xx.json路径为resources/validate/xx.json,则file="/xx.json"
②、若xx.json路径为resources/validate/dir1/dir2/xx.json,则file="/dir1/dir2/xx.json"
2、key的配置
①、填写json文件最外层的key即可(这个不懂怎么表达了)
3、level的配置
level共有两个值PvConst.LEVEL_STRICT、PvConst.LEVEL_LOOSE。在@ParamsValidate上配置level,此level只在当前controller方法上有效,且优先级高于ValidateInterface#getLevel()中配置的值。
三、配置init.json
校验规则中的regex正则表达式有很多是可以共用的,可以将这些公用的正则表达式抽取出来,放到init.json文件中。
注意:
1、文件名必须是init.json。
2、init.json必须放置在ValidateInterface#basePath()目录中。
3、init.json中正则表达式的key必须以 REGEX_ 开头,注意后面有下划线。
举个例子,init.json如下:
{
"REGEX_BOOLEAN": "^(true)$|^(false)$",
"REGEX_CHAR": "^[a-zA-Z0-9_]+$"
}
其他json文件引用init.json中的正则:
"userValidateInit": {
"name": {
"request": true,
"minLength": 10,
"maxLength": 100,
"regex": "REGEX_CHAR",
"message": "名字必须是字母、数字、_"
},
"single": {
"regex": "REGEX_BOOLEAN",
"message": "single必须是true、false"
}
},