在日常开发中,不论是在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<InnerRquest> 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
}
配置拦截器
<aop:config>
<!-- pointcut 配置成需要拦截的路径 -->
<aop:advisor pointcut="execution(*.*(..))" advice-ref="smartValidateInterceptor"/>
</aop:config>
<bean id="smartValidateInterceptor" class="com.smart.validate.interceptor.SmartValidateInterceptor" />
(一)定义规则注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomerValidate {
String message () default "";
String name () default "";
}
(二)实现规则
public class MatchCustomerValidate extends AbstractMatchValidate<CustomerValidate >{
@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=“年龄”,在验证不通过时message为 “年龄为必填项”
@NotNullValidate(name = "年龄")
private Integer age;
//message=“请输入年龄”,在验证不通过时message为 “请输入年龄”
@NotNullValidate(message = "请输入年龄")
private Integer age;
如果name和message都未定义,则message为默认值 “age为必填项”
不论是Spring MVC的REST API参数,还是RPC中的对象入参,一般都会有相应的拦截器去处理日志打印、异常等相关的逻辑,因此参数验证也可以在这一层中使用SmartValidate.validate方法来统一接入,框架中也内置了基于Spring的SmartValidateInterceptor拦截器,也可以自己跟实际情况来实现自己的验证策略。