1. Java中的参数验证(非Spring版)
1.1. 前言
-
为什么我总遇到这种非正常问题,我们知道很多时候我们的参数校验都是放在controller层的传入参数进行校验,我们常用的校验方式就是引入下列的jar包,在参数中添加
@Validated,并对Bean对象的参数做不同的注解处理就行,对Spring这种常用做法大家应该比较熟了 -
但我现在遇到的需求,因为boss追求通用性,我们的controller入口只有一个,是通过传入参数中的不同tradeCode来区分调用哪个服务,这时我校验参数就得放到具体的每个服务方法上了,这样经过我的测试,加该注解已经不起作用了
1<!--jsr 303--> 2<dependency> 3 <groupId>javax.validation</groupId> 4 <artifactId>validation-api</artifactId> 5 <version>1.1.0.Final</version> 6</dependency> 7 8 9@PostMapping("/save/valid") 10public RspDTO save(@RequestBody @Validated UserDTO userDTO) { 11 userService.save(userDTO); 12 return RspDTO.success(); 13}@Data public class UserDTO implements Serializable {
1private static final long serialVersionUID = 1L; 2 3/*** 用户ID*/ 4@NotNull(message = "用户id不能为空") 5private Long userId; 6 7/** 用户名*/ 8@NotBlank(message = "用户名不能为空") 9@Length(max = 20, message = "用户名不能超过20个字符") 10@Pattern(regexp = "^[\\u4E00-\\u9FA5A-Za-z0-9\\*]*$", message = "用户昵称限制:最多20字符,包含文字、字母和数字") 11private String username; 12 13/** 手机号*/ 14@NotBlank(message = "手机号不能为空") 15@Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误") 16private String mobile; 17 18/**性别*/ 19private String sex; 20 21/** 邮箱*/ 22@NotBlank(message = "联系邮箱不能为空") 23@Email(message = "邮箱格式不对") 24private String email; 25 26/** 密码*/ 27private String password; 28 29/*** 创建时间 */ 30@Future(message = "时间必须是将来时间") 31private Date createTime;}
1.2. 方案
- 不能用它的注解,但我们可以用它的方法,下面我写了一个用Java代码验证参数的例子,抛砖引玉,并不能直接用在自己的系统哦,想要使用请结合自己系统封装方法,我打算做成注解的形式,利用spring aop切我的服务层,实现的效果就和controller层类似了
1.2.1. 主方法
1import org.springframework.validation.annotation.Validated; 2 3import javax.validation.ConstraintViolation; 4import javax.validation.Validation; 5import javax.validation.Validator; 6import javax.validation.ValidatorFactory; 7import java.util.Date; 8import java.util.Set; 9import java.util.stream.Stream; 10 11/** 12 * @author laoliangliang 13 * @date 2019/10/22 15:19 14 */ 15public class ValidLearn { 16 public static void main(String[] args) { 17 ValidLearn learn = new ValidLearn(); 18 learn.testValid(new Order().setIdcard("33062119981012361X").setName(" ").setCreateDate(new Date())); 19 } 20 21 public void testValid(@Validated Order order) { 22 ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory(); 23 Validator validator = validatorFactory.getValidator(); 24 //分组Insert.class则id为空不检验 25 Set<ConstraintViolation<Order>> validate = validator.validate(order, Insert.class); 26 Stream.of(validate).forEach(action -> { 27 for (ConstraintViolation<Order> orderConstraintViolation : action) { 28 String message = orderConstraintViolation.getMessage(); 29 System.out.println(message); 30 } 31 }); 32 } 33}
1.2.2. 实体类
1/** 2 * @author laoliangliang 3 * @date 2019/10/21 16:44 4 */ 5@Data 6@Accessors(chain = true) 7public class Order { 8 9 @NotNull(message = "id不能为空",groups = Update.class) 10 private Long id; 11 12 @NotEmpty(message = "name is not null",groups = Insert.class) 13 private String name; 14 15 @Future(message = "必须之后的时间") 16 private Date createDate; 17 18 @IdCardValid(message = "idcard 不合法") 19 private String idcard; 20}
- 以上两个代码就可以做到检验实体类对象注解,并打印校验不通过的消息了,可以改造成存在校验错误消息则抛出异常
- 代码还涉及了一些细节,比如group分组和自定义注解
1.2.3. group分组
1import javax.validation.groups.Default; 2 3/** 4 * @author laoliangliang 5 * @date 2019/10/22 16:32 6 */ 7public interface Update extends Default { 8} 9 10 11import javax.validation.groups.Default; 12 13/** 14 * @author laoliangliang 15 * @date 2019/10/22 16:32 16 */ 17public interface Insert extends Default { 18} 19
- 我例子代码中用到了
Insert.class,表示做插入动作时,存在这个分组的注解才会起作用,因此我id不传,id不为空的注解也不会起作用
1.2.4. 自定义注解
1import javax.validation.ConstraintValidator; 2import javax.validation.ConstraintValidatorContext; 3import java.util.regex.Matcher; 4import java.util.regex.Pattern; 5 6/** 7 * @author laoliangliang 8 * @date 2019/10/22 15:55 9 */ 10public class IdCardValidator implements ConstraintValidator<IdCardValid, Object> { 11 12 private Pattern pattern = Pattern.compile("^[1-9]\\d{7}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])" + 13 "\\d{3}$|^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9]|X)$"); 14 @Override 15 public void initialize(IdCardValid idCardValid) { 16 } 17 18 @Override 19 public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) { 20 Matcher matcher = pattern.matcher(o.toString()); 21 return matcher.matches(); 22 } 23} 24 25 26/** 27 * @author laoliangliang 28 * @date 2019/10/22 15:53 29 */ 30@Documented 31@Target({ElementType.PARAMETER,ElementType.FIELD}) 32@Retention(RetentionPolicy.RUNTIME) 33@Constraint(validatedBy = IdCardValidator.class) 34public @interface IdCardValid { 35 36 String message() default "身份证不合法"; 37 38 Class<?>[] groups() default {}; 39 40 Class<? extends Payload>[] payload() default {}; 41}
- 这里我以验证身份证号为例,写了个自定义注解,实现
ConstraintValidator接口,在isValid方法中实现自定义逻辑即可使用注解
1.3. 总结
- 此篇举了
Validation用Java代码实现验证的例子,应对service层参数验证,实际应用到自己代码可以自己写个自定义注解,实现aop切面,在切面中进行验证