
1.@Validated和@Valid的区别和使用注意事项
1.1来源
1.2注解位置
1.3是否支持分组
1.4是否支持嵌套校验
2.集成依赖
<dependency><groupId>javax.validationgroupId><artifactId>validation-apiartifactId><version>版本号version>dependency><dependency><groupId>org.springframework.bootgroupId><artifactId>spring-boot-starter-webartifactId><version>xxxx.RELEASEversion>dependency><dependency><groupId>org.springframework.bootgroupId><artifactId>spring-boot-starter-validationartifactId>dependency>
3.@Valid常用注解

4.@Validated常用注解

4.自定手机号注解校验
4.1引入依赖
<dependency><groupId>cn.hutoolgroupId><artifactId>hutool-allartifactId><version>5.8.8version>dependency>
4.2实现代码
@Phone注解类
package xxxxx.validator;import javax.validation.Constraint;import javax.validation.Payload;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;// 指定约束处理器,也就是手机号格式验证是哪个类来做校验public Phone {//这里不采用正则表达式做手机号的格式配置//String pattern() default "^(?:(?:\\+|00)86)?1\\d{10}$";String message() default "手机号格式不正确";// groups用来指定分组,可以让校验采取不同的机制,当前默认未指定任何分组机制,默认每次都要进行校验Class>[] groups() default {};Class extends Payload>[] payload() default {};}
PhoneValidator类
package xxx.validator;import cn.hutool.core.lang.Validator;import javax.validation.ConstraintValidator;import javax.validation.ConstraintValidatorContext;import java.util.Objects;/*** 校验处理器:做手机号码格式验证的核心类*/public class PhoneValidator implements ConstraintValidator<Phone, String> {// 注解对象private Phone phone;// 初始化【Phone】对象public void initialize(Phone constraintAnnotation) {phone = constraintAnnotation;}public boolean isValid(String value, ConstraintValidatorContext context) {//获取【Phone】对象的手机格式验证表达式//String pattern = phone.pattern();//Pattern compile = Pattern.compile(pattern);//Matcher matcher = compile.matcher(value);// return matcher.matches();if (Objects.isNull(value)) {throw new RuntimeException("手机号字段不为空");}boolean isMobile = Validator.isMobile(value);return isMobile;}}
5.优雅统一异常处理
GlobalExceptionHandler类
package xxxxx.handler;import cn.dev33.satoken.exception.NotPermissionException;import cn.hutool.core.collection.CollectionUtil;import cn.hutool.core.convert.Convert;import xxxxx.RestResponse;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.JsonMappingException;import lombok.extern.slf4j.Slf4j;import org.springframework.core.MethodParameter;import org.springframework.http.MediaType;import org.springframework.http.converter.HttpMessageConverter;import org.springframework.http.converter.HttpMessageNotReadableException;import org.springframework.http.server.ServerHttpRequest;import org.springframework.http.server.ServerHttpResponse;import org.springframework.validation.BindException;import org.springframework.validation.FieldError;import org.springframework.web.HttpRequestMethodNotSupportedException;import org.springframework.web.bind.MethodArgumentNotValidException;import org.springframework.web.bind.MissingServletRequestParameterException;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.RestControllerAdvice;import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;import javax.validation.ConstraintViolation;import javax.validation.ConstraintViolationException;import javax.validation.ValidationException;import java.time.DateTimeException;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Objects;import java.util.Set;/*** 全局异统一常处理**/public class GlobalExceptionHandler implements ResponseBodyAdvice<Object> {private static final Integer STATUS_404 = 404;public static final String ERROR_MSG_404 = "接口地址不存在";(Exception.class)public RestResponse exceptionHandler(Exception e) {if (e instanceof MissingServletRequestParameterException) {//URL请求参数缺失异常处理带?xxx1=xxx1 & xxx2=xxx2MissingServletRequestParameterException m1 = (MissingServletRequestParameterException) e;return RestResponse.fail("请求参数 " + m1.getParameterName() + " 不能为空");}if (e instanceof ConstraintViolationException) {//@Valid参数校验异常处理ConstraintViolationException e2 = (ConstraintViolationException) e;List<String> s = new ArrayList<>();Set> constraintViolations = e2.getConstraintViolations(); for (ConstraintViolation> c : constraintViolations) {String message = c.getMessage();s.add(message);}if (CollectionUtil.isNotEmpty(s)) {return RestResponse.fail(s);}}if (e instanceof MethodArgumentNotValidException) {//@Validated参数校验异常处理//嵌套对象字段校验MethodArgumentNotValidException e3 = (MethodArgumentNotValidException) e;ListfieldErrors = e3.getBindingResult().getFieldErrors(); if (CollectionUtil.isNotEmpty(fieldErrors)) {Map<String, Object> validError = this.getValidError(fieldErrors);return RestResponse.fail(validError);}}if (e instanceof BindException) {//POST请求@RequestBody/@Validated参数绑定校验异常处理BindException e4 = (BindException) e;ListfieldErrors = e4.getBindingResult().getFieldErrors(); if (CollectionUtil.isNotEmpty(fieldErrors)) {Map<String, Object> validError = this.getValidError(fieldErrors);return RestResponse.fail(validError);}}if (e instanceof HttpRequestMethodNotSupportedException) {//捕获请求方法异常:比如post接口使用了getHttpRequestMethodNotSupportedException e5 = (HttpRequestMethodNotSupportedException) e;String method = e5.getMethod();return RestResponse.fail(method + "请求方法不被允许");}if (e instanceof HttpMessageNotReadableException) {HttpMessageNotReadableException e5 = (HttpMessageNotReadableException) e;Throwable rootCause = e5.getRootCause();//校验参数是否多余,导致不能识别if (rootCause instanceof JsonProcessingException) {JsonMappingException mappingException = (JsonMappingException) rootCause;String fieldName = mappingException.getPath().get(0).getFieldName();return RestResponse.fail("请求body中参数 " + fieldName + " 不能识别");}//校验日期字段转换是否异常if (rootCause instanceof DateTimeException) {return RestResponse.fail("请求body中日期转换异常");}return RestResponse.fail("请求body不为空");}if (e instanceof ValidationException) {ValidationException e6 = (ValidationException) e;Throwable cause = e6.getCause();if (Objects.nonNull(cause)) {return RestResponse.fail(cause.getMessage());}return RestResponse.fail("自定义字段校验注解处理异常");}//上篇文章的sa-token的异常if (e instanceof NotPermissionException) {NotPermissionException e7 = (NotPermissionException) e;log.error("NotPermissionException.msg:{}", e7.getMessage());return RestResponse.fail("没有此权限!");}//可以根据业务拓展业务异常//也可以处理其它异常return RestResponse.fail(e.getMessage());}/*** 获取校验错误信息*/private Map<String, Object> getValidError(ListfieldErrors) { Map<String, Object> result = new HashMap<>(16);for (FieldError error : fieldErrors) {result.put(error.getField(), error.getDefaultMessage());}return result;}// 决定是否执行beforeBodyWrite()方法public boolean supports(MethodParameter methodParameter, Class extends HttpMessageConverter>> aClass) {return true;}public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class extends HttpMessageConverter>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {if (o == null) {return RestResponse.fail();}//String类型需要特殊处理 手动转为json字符串if (o instanceof String) {return RestResponse.success(o);}if (o instanceof RestResponse) {return o;}//boolean类型 返回对应的成功或失败if (o instanceof Boolean) {return RestResponse.success(o);}//404时 返回特定信息 404也可以重写BasicErrorControllerif (is404(o)) {return RestResponse.fail(ERROR_MSG_404 + ":" + STATUS_404);}return o;}private boolean is404(Object o) {if (o instanceof Map) {Map<String, Object> map = Convert.toMap(String.class, Object.class, o);Integer status = Convert.toInt(map.get("status"));return STATUS_404.equals(status);}return false;}}