依赖包:
javax.validation
validation-api
1.1.0.Final
org.hibernate
hibernate-validator
5.2.0.Final
验证对象:
import lombok.Data;
import org.hibernate.validator.constraints.NotBlank;
import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.groups.Default;
@Data
public class BBB {
@NotNull
//只有@Validated 指定了group为RegisterCheckGroup.class时才校验
@Min(value = 3, message = "不能小于3", groups = RegisterCheckGroup.class)
private Integer age;
@NotBlank(groups = {Default.class, LoginCheckGroup.class, RegisterCheckGroup.class})
private String name;
@IDCardValid
private String idCard;
@Valid //级联(递归)验证,验证CCC对象的字段
@NotNull
private CCC ccc;
/**
* 登录验证校验组
*/
interface LoginCheckGroup {//extends javax.validation.groups.Default {
}
/**
* 注册验证校验组
*/
interface RegisterCheckGroup {
}
}
import lombok.Data;
import org.hibernate.validator.constraints.Range;
import javax.validation.constraints.NotNull;
@Data
public class CCC {
@NotNull
@Range(min = 0, max = 2)
private Integer sex;
}
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.NotNull;
@RestController
public class AAA {
//基本类型验证
@RequestMapping("/hi")
public String hi(@NotBlank String name, @NotNull(message = "不能为null") Integer age) {
System.out.println(name + ":" + age);
return "hi " + name + ":" + age;
}
//组合验证
@RequestMapping("/hi2")
public String hi2(@NotBlank String name, @NotNull(message = "不能为null") Integer age, @Validated CCC ccc) {
System.out.println(name + ":" + age + ":" + ccc.toString());
return "hi2 " + name + ":" + age + ":" + ccc.toString();
}
//封装类型验证
@RequestMapping("/ha")
public String ha(@Validated BBB bbb) {
System.out.println(bbb);
return bbb.toString() + " ha";
}
//登录验证组
@RequestMapping("/login")
public String login(@Validated(BBB.LoginCheckGroup.class) BBB bbb) {
System.out.println(bbb);
return bbb.toString() + " ha";
}
//注册验证组
@RequestMapping("/register")
public String register(@Validated(BBB.RegisterCheckGroup.class) BBB bbb) {
System.out.println(bbb);
return bbb.toString() + " ha";
}
}
自定义注解验证:
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IDCardConstraintValidator.class)
public @interface IDCardValid {
String message() default "IDCard格式不正确";
Class>[] groups() default {};
Class extends Payload>[] payload() default {};
}
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class IDCardConstraintValidator implements ConstraintValidator {
@Override
public void initialize(IDCardValid constraintAnnotation) {
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
return IDCardUtil.isIDCard(value);
}
}
参考@NotNull:
package javax.validation.constraints;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* The annotated element must not be {@code null}.
* Accepts any type.
*
* @author Emmanuel Bernard
*/
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = { })
public @interface NotNull {
String message() default "{javax.validation.constraints.NotNull.message}";
Class>[] groups() default { };
Class extends Payload>[] payload() default { };
/**
* Defines several {@link NotNull} annotations on the same element.
*
* @see javax.validation.constraints.NotNull
*/
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@interface List {
NotNull[] value();
}
}
全局异常处理器
package com.base.exception;
import com.base.utils.ResponseUtil;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
@Component
public class GlobalExceptionHandler extends DefaultHandlerExceptionResolver {
@Override
public int getOrder() {
return super.getOrder() + 1;
}
@Override
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
try {
//前端参数(解析或效检)异常
//补充-默认异常处理器没有返回以下异常的错误信息
if (ex instanceof ControllerArgsCheckException) {
return this.handleBadRequestException(ex, request, response, handler);
}
} catch (Exception e) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", e);
}
}
return super.doResolveException(request, response, handler, ex);
}
protected ModelAndView handleBadRequestException(Exception ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
return handleBadRequestException(ex, request, response, handler, ex.getMessage());
}
protected ModelAndView handleBadRequestException(Exception ex, HttpServletRequest request, HttpServletResponse response, Object handler, String message) throws IOException {
return handleHttpMessageException(ex, request, response, handler, message, HttpServletResponse.SC_BAD_REQUEST);
}
protected ModelAndView handleHttpMessageException(Exception ex, HttpServletRequest request, HttpServletResponse response, Object handler, String message, Integer status) throws IOException {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Failed to handle HTTP message: " + ex);
}
return ResponseUtil.write(request, response, status, message);
}
@Override
protected ModelAndView handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
return handleBadRequestException(ex, request, response, handler);
}
@Override
protected ModelAndView handleServletRequestBindingException(ServletRequestBindingException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
return handleBadRequestException(ex, request, response, handler);
}
@Override
protected ModelAndView handleTypeMismatch(TypeMismatchException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
String message;
if (ex instanceof MethodArgumentTypeMismatchException) {
message = jointArgErrorTip(((MethodArgumentTypeMismatchException) ex).getName(), ex.getValue(), ex.getMessage());
return handleBadRequestException(ex, request, response, handler, message);
}
return handleBadRequestException(ex, request, response, handler);
}
@Override
protected ModelAndView handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
return handleBadRequestException(ex, request, response, handler);
}
@Override
protected ModelAndView handleMissingServletRequestPartException(MissingServletRequestPartException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
return handleBadRequestException(ex, request, response, handler);
}
@Override
protected ModelAndView handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
String message = jointArgErrorTip(ex.getBindingResult().getFieldErrors());
return handleBadRequestException(ex, request, response, handler, message);
}
@Override
protected ModelAndView handleBindException(BindException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
String message = jointArgErrorTip(ex.getFieldErrors());
return handleBadRequestException(ex, request, response, handler, message);
}
/**
* 拼接参数错误提示
*/
public static String jointArgErrorTip(List fieldErrorList) {
// 只返回第一个参数错误提示
FieldError fieldError = fieldErrorList.get(0);
return jointArgErrorTip(fieldError.getField(), fieldError.getRejectedValue(), fieldError.getDefaultMessage());
}
/**
* 拼接参数错误提示
*/
public static String jointArgErrorTip(String name, Object value, String message) {
return jointTip("参数校验错误: ", name, value, message);
}
/**
* 拼接错误提示
*/
public static String jointTip(String prefix, String name, Object value, String message) {
return prefix + name + " = [" + value + "], " + message + "; ";
}
}
扩展参数校验:
import com.wopuwulian.common.enums.ValueMsgEnum;
import lombok.Getter;
@Getter
public class ControllerArgsCheckException extends RuntimeException {
private String code;
public ControllerArgsCheckException(String message) {
super(message);
}
public ControllerArgsCheckException(String code, String message) {
super(message);
this.code = code;
}
public ControllerArgsCheckException(ValueMsgEnum valueMsgEnum) {
this(String.valueOf(valueMsgEnum.getValue()), valueMsgEnum.getMsg());
}
}
org.aspectj
aspectjweaver
1.8.5
org.aspectj
aspectjrt
1.8.6
import com.base.exception.ControllerArgsCheckException;
import com.base.exception.GlobalExceptionHandler;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.validation.BeanPropertyBindingResult;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.ValidatorFactory;
import javax.validation.executable.ExecutableValidator;
import java.lang.reflect.Method;
import java.util.Set;
/**
* controller层参数校验
*
* @Description
* @Author zhongxing
* @Date 2020/4/20 17:29
* @Version 1.0
*/
@Slf4j
@Aspect
@Component
public class ArgumentValidAspect {
private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
private final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
private final ExecutableValidator validator = factory.getValidator().forExecutables();
private Set> validMethodParams(T obj, Method method, Object[] params) {
return validator.validateParameters(obj, method, params);
}
@Pointcut("execution(public * org.xxx.ac.*.controller.*.*(..)) || execution(public * com.xxx.wechat.controller.*.*(..))")
public void valid() {
}
@Around("valid()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
//无参数不校验
Object[] objects = pjp.getArgs();
if (objects.length == 0) {
return pjp.proceed();
}
// **************************校验基本类型参数*************************/
// 获得切入目标对象
Object target = pjp.getThis();
// 获得切入的方法
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
// 执行校验,获得校验结果
Set> validResult = validMethodParams(target, method, objects);
//如果有校验不通过的
if (!validResult.isEmpty()) {
// 获得方法的参数名称
String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
// 只返回第一个参数错误提示
ConstraintViolation
import com.base.exception.ControllerArgsCheckException;
import com.base.exception.GlobalExceptionHandler;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.hibernate.validator.internal.engine.path.NodeImpl;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.validation.BeanPropertyBindingResult;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.ValidatorFactory;
import javax.validation.executable.ExecutableValidator;
import java.lang.reflect.Method;
import java.util.Set;
/**
* service接口层(dubbo调用方)参数校验
*
* @Description
* @Author zhongxing
* @Date 2020/4/20 17:29
* @Version 1.0
*/
@Slf4j
@Aspect
//@Component 未启用
public class ArgumentValidAspectService {
private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
private final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
private final ExecutableValidator validator = factory.getValidator().forExecutables();
private Set> validMethodParams(T obj, Method method, Object[] params) {
return validator.validateParameters(obj, method, params);
}
@Pointcut("execution(public * org.xxx.ac.*.service.*Service.*(..))")
public void valid() {
}
@Around("valid()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
//无参数不校验
Object[] objects = pjp.getArgs();
if (objects.length == 0) {
return pjp.proceed();
}
// **************************校验基本类型参数*************************/
// 获得切入目标对象
Object target = pjp.getThis();
// 获得切入的方法
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
// 执行校验,获得校验结果
Set> validResult = validMethodParams(target, method, objects);
//如果有校验不通过的
if (!validResult.isEmpty()) {
// 只返回第一个参数错误提示
ConstraintViolation