前言
现在公司中 Springboot 框架中字段校验使用Assert
但遇到要校验多个字段,一个字段多重限制,那么这时候Assert
便变得很笨重,且不优雅
这时候JSR 303
和JSR 349
闪亮登场 (~ ̄▽ ̄)~
一、最佳实践
配置
版本:Spring boot 2.0
如果开发web,spring-boot-starter-web
包中就有hibernate-validate
包。
当然,不导入这个包,导入spring-boot-starter-validation
也可以
(1)整合配置
为了能与原先
web
配置(拦截器、过滤器等)统一管理,希望他们能在同一文件或同一文件夹下。
-
为了能使用 Spring MVC 部分特性,可能会选择使用继承(
extend
)WebMvcConfigurerAdapter
,然而这个已经过时了@Deprecated
-
突然发现还有个
WebMvcConfigurationSupport
,也要extend
,欣喜若狂。然而发现继承这个会将配置完全交给 Spring MVC,这样会与自定义的配置和Spring boot的某些配置相冲突。 -
最后种,引用(
implements
)WebMvcConfigurer
,解决了我的需求。同时我发现
public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer
…
o(╥﹏╥)o
- 使用
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)常用搭配
- 字符串
String
@Length
和@Size
均可
@NotBlank(message = "请填写项目名称")
@Length(min = 3, max = 50, message = "项目名称长度大于3且小于50")
private String projectName;
- 整型
Integer
@Range
和@Max
@Min
都可
@NotNull(message = "请选择项目背景")
@Digits(integer = 1, fraction = 0, message = "请选择项目背景")
@Range(min = 0, max = 3, message = "请重新选择项目背景")
private Integer projectType;
- 用于小数
BigDecimal
@NotNull(message = "请填写融资金额")
@Digits(integer = 10, fraction = 5, message = "请填写正确的金额")
private BigDecimal amount;
- Boolean 校验
@AssertTrue 验证 Boolean 对象是否为 true
@AssertFalse 验证 Boolean 对象是否为 false
- 对象数据校验
@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
层使用
@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;
}
}