一个非常实用的Java参数验证框架
简介
在日常开发中,不论是在REST API还是RPC接口在代码上都会做参数校验,用来验证输入参数的合法性,举个最简单的例子:
(一)验证Spring MVC接口入参
@RequestMapping(value = "helloworld")
@ResponseBody
public Object helloworld(String str) throws Exception {
if(str == null || str.trim().length() == 0) {
throw new IllegalArgumentException("str is required");
}
}
Spring MVC 中也可以使用 @RequestParam(required = true) 注解来约束str参数为必填项,但这个框架层会做拦截,被拦截后的效果一般不是预期想要的
(二)验证RPC接口中的入参
class Request {
String id;
String name;
getter ……
setter ……
}
public Response helloworld(Request request) {
if(StringUtils.isEmpty(request.getId()) || StringUtils.isEmpty(request.getName())) {
throw new IllegalArgumentException("id or name is required");
}
}
一个非空判断需要写一行代码、如果需要验证的参数很多或者还需要判断参数长度、取值范围、集合大小等,又或者参数是一个对象,对象里面循环嵌套了对象,每个对象中又有很多字段需要验证参数的合法性等等,这个时候如果仍然使用hard code来实现的话,实现、维护成本就太高了,而且代码的可读性也是非常糟糕的,因此给大家分享下Smart-Validate验证框架,用来解决以上的问题。
Smart-Validate,一个非常实用的Java参数验证框架,具有以下特点:
足够轻,整个代码量不足500行
基于注解声明规则,使用成本低
内置常见的验证规则
支持嵌套对象的验证
可自由拓展验证规则
框架内置以下规则列表
规则注解
说明
支持的数据类型
MaxLengthValidate
验证最大长度
String、Collection、Map、Array
MaxValueValidate
验证最大值
Integer、Long、Byte、Short、Double、Float
MinLengthValidate
验证最小长度
String、Collection、Map、Array
MinValueValidate
验证最小值
Integer、Long、Byte、Short、Double、Float
NotNullValidate
验证非空
Object、String
RangeLengthValidate
验证长度范围
String、Collection、Map、Array
RangeValueValidate
验证值范围
Integer、Long、Byte、Short、Double、Float
RegexpValidate
验证正则规则
String
使用方法
验证对象参数
@ValidateBean
public class Request {
//ID不能为空
@NotNullValidate
private String id;
//name最小长度为5
@MinLengthValidate(length = 5)
private String name;
public Request() {}
getter ……
setter ……
}
public void helloworld(Request request) throws SmartValidateException {
SmartValidate.validate(request);
//code
}
public static void main(String[] args) {
try {
Request req = new Request();
req.setId(null);
req.setName("haha");
new Demo().helloworld(req);
} catch (SmartValidateException e) {
System.err.println(e.getMessage());
}
}
LOG Print: id为必填项
public static void main(String[] args) {
try {
Request req = new Request();
req.setId("1");
req.setName("haha");
new Demo().helloworld(req);
} catch (SmartValidateException e) {
System.err.println(e.getMessage());
}
}
LOG Print: name的长度不能小于5
验证嵌套对象参数
@ValidateBean
public class Request {
@NotNullValidate
private InnerRquest param;
getter ……
setter ……
}
@ValidateBean
public class InnerRquest {
@NotNullValidate
private String id;
@MaxValueValidate(value = "99")
private Integer age;
public InnerRquest() {}
getter ……
setter ……
}
public void helloworld(Request request) throws SmartValidateException {
SmartValidate.validate(request);
//code
}
public static void main(String[] args) {
try {
Request req = new Request();
InnerRquest ireq = new InnerRquest();
ireq.setId("1");
ireq.setAge(100);
req.setParam(ireq);
new Demo().helloworld(req);
} catch (SmartValidateException e) {
System.err.println(e.getMessage());
}
}
LOG Print: age的值不能大于99
验证集合中的对象
@ValidateBean
public class Request {
@NotNullValidate
private List param;
getter ……
setter ……
}
public void helloworld(Request request) throws SmartValidateException {
SmartValidate.validate(request);
//code
}
public static void main(String[] args) {
try {
Request req = new Request();
InnerRquest ireq = new InnerRquest();
ireq.setId("1");
ireq.setAge(100);
req.setParam(Arrays.asList(ireq));
new Demo().helloworld(req);
} catch (SmartValidateException e) {
System.err.println(e.getMessage());
}
}
LOG Print: age的值不能大于99
验证方法中的参数列表
public void helloworld(
@ValidateArgument(
notNull=@NotNullValidate,
maxLength=@MaxLengthValidate(length=1)
)String str) throws Exception {
//code
}
配置拦截器
拓展自定义验证规则
(一)定义规则注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomerValidate {
String message () default "";
String name () default "";
}
(二)实现规则
public class MatchCustomerValidate extends AbstractMatchValidate{
@Override
public void validate(CustomerValidate t,
String fieldName,
Object value)
throws SmartValidateException {
//your code
}
}
(三)生效规则
ValidateRulePool.mount(CustomerValidate.class, new MatchCustomerValidate());
(四)使用 @CustomerValidate 注解来验证参数
使用多个验证规则
不论是参数列表还是对象中的字段,都可以同时支持多个验证规则。
(一)对象参数
@NotNullValidate
@RangeValueValidate(min = "1", max = "100")
private Integer age;
(二)方法参数
public void helloworld(
@ValidateArgument(
notNull=@NotNullValidate,
rangeValue=@RangeValueValidate(min = "1", max = "100")
)Integer age) throws Exception {}
自定义提示消息
内置的验证规则均支持自定义message,以NotNullValidate为例,在注解中声明可以声明name或message属性,例如:
自定义name属性
//name=“年龄”,在验证不通过时message为 “年龄为必填项”
@NotNullValidate(name = "年龄")
private Integer age;
自定义message属性
//message=“请输入年龄”,在验证不通过时message为 “请输入年龄”
@NotNullValidate(message = "请输入年龄")
private Integer age;
如果name和message都未定义,则message为默认值 “age为必填项”
最佳实践
不论是Spring MVC的REST API参数,还是RPC中的对象入参,一般都会有相应的拦截器去处理日志打印、异常等相关的逻辑,因此参数验证也可以在这一层中使用SmartValidate.validate方法来统一接入,框架中也内置了基于Spring的SmartValidateInterceptor拦截器,也可以自己跟实际情况来实现自己的验证策略。
源码