validator百度博客一大堆,本文是摘取的部分內(nèi)容結(jié)合在項(xiàng)目中使用的經(jīng)驗(yàn).
一.導(dǎo)入maven依賴
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.0.Final</version>
</dependency>
二.給需要校驗(yàn)的pojo加上注解
@Data
public class InformationBasicInfoAO {
/**
* 主鍵id
*/
private Long id;
/**
* 文章標(biāo)題
*/
@NotBlank(message = "文章標(biāo)題不能為空")
private String title;
/**
* 發(fā)布日期
*/
@NotNull(message = "發(fā)布時(shí)間不能為空")
private Date releaseDate;
/**
* 文章來源
*/
@NotBlank(message = "文章來源不能為空")
private String source;
message的內(nèi)容是返回給前端的提醒信息,可以不填
三.校驗(yàn)
1.通用的校驗(yàn)工具
@Slf4j
public class ValidatorUtil {
//也可以使用spring注入的方式
private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
/**
* 使用指定分組
*
* @param object 被校驗(yàn)的bean
* @param groups 分組
* @return
*/
public static <T> Map<String, StringBuilder> validate(T object, Class<?>... groups) {
Map<String, StringBuilder> errorMap = new HashMap<>(16);
if (groups == null) {
groups = new Class[]{Default.class};
}
Set<ConstraintViolation<T>> set = validator.validate(object, groups);
if (CollectionUtils.isEmpty(set)) {
return null;
}
String property;
for (ConstraintViolation<T> c : set) {
// 這里循環(huán)獲取錯(cuò)誤信息,可以自定義格式
property = c.getPropertyPath().toString();
if (errorMap.get(property) != null) {
errorMap.get(property).append(",").append(c.getMessage());
} else {
StringBuilder sb = new StringBuilder();
sb.append(c.getMessage());
errorMap.put(property, sb);
}
}
return errorMap;
}
}
2.使用工具校驗(yàn)
這種方式直接拋去異常,全局異常處理
Map<String, StringBuilder> errorMap = ValidatorUtil.validate(object);
if (!CollectionUtils.isEmpty(errorMap)) {
log.info("------參數(shù)校驗(yàn)失敗:{}", errorMap);
throw new ApplicationException(new ExceptionContext("參數(shù)校驗(yàn)失敗"), "000005", errorMap.toString());
}
也可以在controller中返回.
Map<String, StringBuilder> errorMap = ValidatorUtils.validate(accountAO);
if (!CollectionUtils.isEmpty(errorMap)) {
simpleData.setData(errorMap);
result.setContent(simpleData);
return result;
}
也可以在controller的形參上使用注解@Valid,這種方式不需要util類,可以直接使用,直接拋出異常,需要加配置項(xiàng),具體可以百度,個(gè)人不喜歡這種方式,不夠靈活.
@PostMapping(value = "/order")
public PojoResult<String> purchase(@RequestBody @Valid OrderAO orderAO,
HttpServletRequest request) throws IOException, ServletException {
PojoResult<String> pojoResult = new PojoResult<>();
}
三.自定義注解校驗(yàn)
框架自帶的注解只能解決一些常規(guī)的校驗(yàn),一些復(fù)雜邏輯的校驗(yàn),可以使用自定義校驗(yàn)器,這是本人很喜歡的功能,重復(fù)的復(fù)雜的校驗(yàn)邏輯可以使用注解很優(yōu)雅的代替,讓代碼的耦合性降低,也更簡潔.
自定義校驗(yàn)的基本思路是--自定義注解+自定義校驗(yàn)器
自定義注解
//這里可以加上原生的注解,直接使用非空等功能
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
//這里需要引入校驗(yàn)器
@Constraint(validatedBy = CheckTagsValidator.class)
@Documented
public @interface CheckTags {
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
自定義校驗(yàn)器
//接口的兩個(gè)泛型,分別是自定義注解和注解校驗(yàn)的參數(shù)類型
public class CheckTagsValidator implements ConstraintValidator<CheckTags, List<String>> {
@Override
public void initialize(CheckTags constraintAnnotation) {
}
//方法體內(nèi)可以寫自己需要校驗(yàn)的邏輯,返回值為false則校驗(yàn)不通過
@Override
public boolean isValid(List<String> value, ConstraintValidatorContext context) {
boolean isValid = false;
//限定最多3個(gè)tag,最少可以0個(gè),tag不超過4個(gè)漢字
if (CollectionUtils.isEmpty(value)) {
return true;
}
String filter = value.stream().filter(tag -> tag.length() > 4).findAny().orElse(null);
if (StringUtils.isBlank(filter) && value.size() < 4) {
isValid = true;
}
if (!isValid) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("標(biāo)簽輸入錯(cuò)誤").addConstraintViolation();
}
return isValid;
}
}
結(jié)合ValidatorUtil使用
四.Controller中校驗(yàn)參數(shù)
Validator本身是支持用@Valid注解自動校驗(yàn)的,也可可以配置校驗(yàn)返回策略,如下
@Configuration
public class ValidatorConfiguration {
@Bean
public Validator validator(){
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure()
.addProperty( "hibernate.validator.fail_fast", "true" )
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
return validator;
}
}
個(gè)人并不推薦這種方式,參數(shù)不對時(shí)會直接拋出異常,然后@ControllerAdvice全局異常處理,返回結(jié)果
有兩個(gè)弊端,一是try/catch相對而言性能較低,二是返回異常結(jié)果不夠清晰;
推薦使用ValidatorUtil結(jié)合AOP,直接返回結(jié)果,代碼如下
@Slf4j
@Aspect
@Component
public class ValidatorAspect {
private static final String METHOD_POST = "POST";
@Around("execution(* com.test.government.affair.controller..*(..))")
public Object handlerControllerMethod(ProceedingJoinPoint point) throws Throwable {
long s = System.currentTimeMillis();
// 從獲取RequestAttributes中獲取HttpServletRequest的信息
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = (HttpServletRequest) requestAttributes
.resolveReference(RequestAttributes.REFERENCE_REQUEST);
BucSSOUser user = SimpleUserUtil.getBucSSOUser(request);
Object[] args = point.getArgs();
//校驗(yàn)post請求參數(shù)
log.info("Controller: 請求 URI( {} )| user( {} )| args:( {} )| ", request.getRequestURI(), user.getEmpId(), args);
if (METHOD_POST.equals(request.getMethod())) {
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
Map<String, StringBuilder> errorMap = ValidatorUtil.validate(arg);
if (!CollectionUtils.isEmpty(errorMap)) {
log.info("參數(shù)校驗(yàn)失敗 errorMap:{}", errorMap);
return ServiceResult.error("000005", errorMap.toString());
}
}
}
Object proceed = point.proceed();
log.info("Controller: 響應(yīng) result:( {} )| 請求耗時(shí):( {} )", proceed, System.currentTimeMillis() - s);
return proceed;
}
}
統(tǒng)一參數(shù)校驗(yàn),同時(shí)打印log