【注解】用注解来优雅校验

前言
现在公司中 Springboot 框架中字段校验使用Assert


但遇到要校验多个字段,一个字段多重限制,那么这时候Assert便变得很笨重,且不优雅


这时候 JSR 303JSR 349 闪亮登场 (~ ̄▽ ̄)~

一、最佳实践


配置
版本:Spring boot 2.0


如果开发web,spring-boot-starter-web包中就有 hibernate-validate包。
当然,不导入这个包,导入 spring-boot-starter-validation 也可以

(1)整合配置

为了能与原先web配置(拦截器、过滤器等)统一管理,希望他们能在同一文件或同一文件夹下。

  1. 为了能使用 Spring MVC 部分特性,可能会选择使用继承(extend)WebMvcConfigurerAdapter,然而这个已经过时了 @Deprecated

  2. 突然发现还有个 WebMvcConfigurationSupport ,也要extend,欣喜若狂。然而发现继承这个会将配置完全交给 Spring MVC,这样会与自定义的配置和Spring boot的某些配置相冲突。

  3. 最后种,引用(implementsWebMvcConfigurer,解决了我的需求。同时我发现
    public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer

o(╥﹏╥)o

  1. 使用JSR 303校验,我可能需要:
  • 快速失败功能(即:遇到错误直接返回,而不是把所有的都校验一般,这样减少CPU资源使用)
  • 我想让其在方法之间传递参数时候也能生效

这样我的配置大概形成了:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(timeInterceptor()).addPathPatterns("/**");
    }

    @Bean
    public FilterRegistrationBean timeFilter() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();

        return registrationBean;
    }

    @Bean
    public TimeInterceptor timeInterceptor() {
        return new TimeInterceptor();
    }

	// 设置校验器
    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
        processor.setValidator(validator());
        return processor;
    }

	// 设置模式
    @Bean
    public Validator validator(){
        // failFast(true)启动快速失败
        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class).
                configure().
                failFast(true).
                buildValidatorFactory();
        return validatorFactory.getValidator();
    }

}


(2)异常捕获

@ControllerAdvice
public class RestExceptionHandler {

    /**
     * 校验JSR-303
     * @param exception
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public ServerResponse validate(ConstraintViolationException exception) {
        log.error(exception.getMessage());
        exception.printStackTrace();
        Iterator iter = exception.getConstraintViolations().iterator();
        ConstraintViolation constraintViolation = (ConstraintViolation) iter.next();
        
        // ServerResponse.createByErrorCodeMessage 为自己配置的返回包装体
        return ServerResponse.createByErrorCodeMessage(Const.HttpStatusCode.BAD_REQUEST.getCode(),
                constraintViolation.getConstraintDescriptor().getMessageTemplate());
    }

}


(3)常用搭配

  1. 字符串 String
    @Length@Size 均可
@NotBlank(message = "请填写项目名称")
@Length(min = 3, max = 50, message = "项目名称长度大于3且小于50")
private String projectName;
  1. 整型 Integer
    @Range@Max @Min 都可
@NotNull(message = "请选择项目背景")
@Digits(integer = 1,  fraction = 0, message = "请选择项目背景")
@Range(min = 0, max = 3, message = "请重新选择项目背景")
private Integer projectType;
  1. 用于小数 BigDecimal
@NotNull(message = "请填写融资金额")
@Digits(integer = 10, fraction = 5, message = "请填写正确的金额")
private BigDecimal amount;
  1. Boolean 校验
@AssertTrue     验证 Boolean 对象是否为 true  
@AssertFalse    验证 Boolean 对象是否为 false  
  1. 对象数据校验
    @Valid可校验对象里面的属性,同时可递归校验
@Valid
Person person;


二、实际运用


(1)Controller 层使用

controller层 校验对象参数

@RequestMapping(value = "/user")
@RestController
public class UserController {
    
    @Autowired
    private UserService userService;

    @PostMapping
    public User getUser(@Valid User user, BindingResult errors) {
        
        if (errors.hasErrors()) {
            errors.getAllErrors().stream().forEach(
                error -> System.out.println(error.getDefaultMessage())
            );
        }

        user.setUsername("Donald");
        return user;
    }
    
}

User对象

@Data
public class User {
    
    @NotBlank(message = "用户类中用户名不能为空")
    private String username;

    @NotBlank(message = "密码不能为空")
    private String password;

    @NotNull(message = "生日不能为空")
    private Date birthday;

}


(2) Service 层使用

  1. @Validated 是 Spring boot 对 @Valid 的增强

UserService.java

@Validated
public interface UserService {

    public List<User> getUserListByName(@NotBlank(message = "用户名不能为空")
                                        @Length(min = 3, max = 10, message = "用户名长度大于3小于10")String username,
                                        @Valid User user);

}

UserServiceImpl.java

@Service
public class UserServiceImpl implements UserService {
     @Override
    public List<User> getUserListByName(String username, User _user) {
    
        List<User> users = new ArrayList<>();
        User user = new User();
        users.add(user);
        return users;
        
    }

}


(3)自定义注解

以 校验手机号 举个栗子

添加注解

@NotBlank(message = "手机号码不能为空")
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {IsMobileValidator.class})
public @interface IsMobile {

    boolean require() default false;

    String message() default "手机号码格式错误";

    Class<?>[] groups() default{};

    Class<?extends Payload>[] payload() default{};
}

添加校验器

public class IsMobileValidator implements ConstraintValidator<IsMobile, String> {

    private static final String regExp = "^((13[0-9])|(15[^4])|(18[0-9])|(17[0-9])|(147))\\d{8}$";

    public boolean isValid(String value, ConstraintValidatorContext context) {

        Pattern p = Pattern.compile(regExp);

        Matcher m = p.matcher(value);

        return m.matches();

    }

}

测试:

@Validated
@RequestMapping(value = "/user")
@RestController
public class UserController {

    @GetMapping("/phone")
    public String getUserPhone(@IsMobile String phone) {

        return phone;
    }
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值