Java使用@Validated注解进行参数验证的方法

目前项目中大部分代码进行参数验证都是写代码进行验证,为了提升方便性和代码的简洁性,所以整理了下使用注解进行参数验证。使用效果如下:

// 要验证的实体类
@Data
public class User implements Serializable {
  @NotBlank(message = "id不能为空!",groups = Update.class)
  protected String id = "";
  @NotBlank(message = "商户id不能为空!")
  protected String tenantId;
  @NotBlank(message = "名称不能为空!")
  protected String name = "";
}
// controller
// 在要验证的参数上添加@Validated注解即可
@PostMapping("add")
public boolean addUser(@Validated @RequestBody User user) {
    ProdAllotStandard info = allotStandardService.getProdAllotStandardWithDetailById(id);
    return info;
}
// 修改则需要比添加的基础上多验证一个id,可以通过group的方式进行区分
// 实体类上不设置groups,默认会是Default,所以修改的时候,我们需要指定Update和Default这两个组
// group是可以自定义的,我默认定义了Add,Update,Delete,Search这四个组
@PostMapping("update")
public boolean updateUser(@Validated({Update.class, Default.class}) @RequestBody User user) {
    ProdAllotStandard info = allotStandardService.getProdAllotStandardWithDetailById(id);
    return info;
}
// 当然该注解也支持service上实现,同样的使用方法,在service的实现类的参数上加上对应注解即可,如:
public boolean addUser(@Validated User user) {

}
// 通过不同group的搭配,我们就可以灵活的在实体类上配置不同场景的验证了
// group为一个接口,用以下方式创建
public interface Add {
}

下面看一下具体的实现,验证框架的核心实现采用的是hibernate-validator,我采用的是5.4.3.Final版本。

controller和service的实现方式不同,下面分别介绍下。

不管哪种实现,我们都需要先定义一个异常和一个异常拦截器,当参数校验出现问题时,我们就抛出对应异常。

// 为了简短,省略了部分代码
public class ParamsException extends RuntimeException{
  private String code;
  public BusinessException() {
  }
  public ParamsException(String code,String message) {
    super(message);
    this.code = code;
  }
  public ParamsException(String message) {
    super(message);
  }
}
// 定义异常处理类
@ControllerAdvice
public class ExceptionAdvice {

  private static Logger L = LoggerFactory.getLogger(ExceptionAdvice.class);

  // 对所有的ParamsException统一进行拦截处理,如果捕获到该异常,则封装成MessageBody返回给前端
  @ExceptionHandler(value = ParamsException.class)
  @ResponseStatus(HttpStatus.OK)
  @ResponseBody
  public MessageBody handleParamsException(HttpServletRequest request, BusinessException e){
    L.error(e.getMessage(),e);
    return getErrorMessageBody(e.getData(),e.getMessage(),e.getMessage(),
        StringUtils.isEmpty(e.getCode())?ResponseCode.BUSINESS_ERROR:e.getCode());
  }
  private MessageBody getErrorMessageBody(Object data,String message,String errorInfo,String code){
    MessageBody body = new MessageBody();
    body.setCode(code);
    body.setData(data);
    body.setErrorCode(Integer.parseInt(code));
    body.setErrorInfo(errorInfo);
    body.setMessage(message);
    body.setSuccess(false);
    return body;
  }
}

controller的验证的实现:

主要是通过实现spring mvc给我们留下的接口进行实现的,该方案没有用到反射和代理。

1.  实现spring提供的SmartValidator接口

public class ParamsValidator implements SmartValidator {

  private javax.validation.Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

  @Override
  public boolean supports(Class<?> clazz) {
    return true;
  }
  // 注解上没有有group的验证逻辑
  @Override
  public void validate(Object target, Errors errors) {
    validate(target,errors,null);
  }
  // 注解上带有group的验证逻辑
  // 第一个参数为我们要验证的参数,第二个不用管,第三个为注解上设置个groups
  @Override
  public void validate(Object target, Errors errors, Object... validationHints) {
    // 这里面为验证实现,可以根据自己的需要进行完善与修改
    if (target == null) {
      throw new ParamsException("参数不能为空!");
    } else {
      if(target instanceof String) {
        if(StringUtils.isEmpty(target)) {
          throw new ParamsException("参数不能为空!");
        }
      }else if(target instanceof Collection) {
        for(Object o:(Collection)target){
          validate(o,validationHints);
        }
      }else {
        validate(target,validationHints);
      }

    }

  }
  private void validate(Object target,Object ... objs) {
    Set<ConstraintViolation<Object>> violations;
    // 没有groups的验证
    if(objs==null || objs.length==0) {
      violations = validator.validate(target);
    } else {
    // 基于groups的验证
      Set<Class<?>> groups = new LinkedHashSet<Class<?>>();
      for (Object hint : objs) {
        if (hint instanceof Class) {
          groups.add((Class<?>) hint);
        }
      }
      violations = validator.validate(target, ClassUtils.toClassArray(groups));
    }
    // 若为空,则验证通过
    if(violations==null||violations.isEmpty()) {
      return;
    }
    // 验证不通过则抛出ParamsException异常。
    for(ConstraintViolation item:violations) {
      throw new ParamsException(item.getMessage());
    }
  }
}

2. 配置并设置Validator验证器

@Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter{
  // 我们在这里重写spring的一个方法,返回我们自定义的验证器
  @Override
  public Validator getValidator() {
    return createValidator();
  }
  @Bean
  public ParamsValidator createValidator(){
    return new ParamsValidator();
  }
}

非常简单,通过上面配置,就可以在controller上使用注解了。

spring mvc对这个注解的处理主要是在RequestResponseBodyMethodProcessor这个类中的resolveArgument方法实现的,该类主要处理方法的参数和返回值。

spring mvc调用的一段代码。

protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
  Annotation[] annotations = parameter.getParameterAnnotations();
  for (Annotation ann : annotations) {
    Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
    if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
      Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
      Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
      binder.validate(validationHints);
      break;
    }
  }
}

service的验证的实现:

该方案主要通过aop进行拦截处理的。如下配置:

@Component
@Aspect
public class ParamsValidateAdvice {
  // 这里重用上面定义的那个validator
  private ParamsValidator validator = new ParamsValidator();
  /**
   * 拦截参数上加了@Validated的注解的方法
   * 排除掉controller,因为controller有自己的参数校验实现 不需要aop
   */
  @Pointcut("execution(* com.choice..*(..,@org.springframework.validation.annotation.Validated (*), ..)) && " +
      "!execution(* com.choice..api..*(..)) && " +
      "!execution(* com.choice..controller..*(..)) ")
  public void pointCut(){}

  @Before("pointCut()")
  public void doBefore(JoinPoint joinPoint){
    Object[] params=joinPoint.getArgs();
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    Method method = signature.getMethod();
    Parameter[] parameters = method.getParameters();
    // 验证参数上的注解
    for(int i=0;i<parameters.length;i++) {
      Parameter p = parameters[i];
      // 获取参数上的注解
      Validated validated = p.getAnnotation(Validated.class);
      if(validated==null) {
        continue;
      }
      // 如果设置了group
      if(validated.value()!=null && validated.value().length>0) {
        validator.validate(params[i],null,validated.value());
      } else {
        validator.validate(params[i],null);
      }
    }
  }
}

这样就可以在service使用验证注解了,具体的Pointcut可以自己进行配置。

个人觉得参数校验在controller或者对外暴露的服务中去做就好了,因为这些都是对外提供服务的,controller层也应该去做这些,所以参数需要校验好。

没必要在自己内部调用的service中加校验。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 详解Spring 参数验证@Validated和@Valid的区别

    Spring Validation验证框架对参数的验证机制提供了@Validated(Spring's JSR-303 规范,是标准 JSR-303 的一个变种),javax提供了@Valid(标准JSR-303规范),配合 BindingResult 可以直接提供参数验证结果.其中对于字段的特定验证注解比如 @NotNull 等网上到处都有,这里不详述 在检验 Controller 的入参是否符合规范时,使用 @Validated 或者 @Valid 在基本验证功能上没有太多区别.但是在分组.

  • Java使用@Validated注解进行参数验证的方法

    目前项目中大部分代码进行参数验证都是写代码进行验证,为了提升方便性和代码的简洁性,所以整理了下使用注解进行参数验证.使用效果如下: // 要验证的实体类 @Data public class User implements Serializable { @NotBlank(message = "id不能为空!",groups = Update.class) protected String id = ""; @NotBlank(message = "商户i

  • SpringBoot @Validated注解实现参数分组校验的方法实例

    前言 在前后端分离开发的时候我们需要用到参数校验,前端需要进行参数校验,后端接口同样的也需要,以防传入不合法的数据. 1.首先还是先导包,导入pom文件. <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> 2.解释一下注解的作用 @N

  • java实现表单必填参数验证的方法

    一. 概述 在开发后端接口, 通常都会涉及检验参数必填校验, 一般我们的处理都是很粗暴的写个if()判断, 然后抛异常. 本文将介绍通过代理的思想, 用注解优雅的处理非空判断 二. 实现过程 最终想要的效果->在方法的参数加个注解或者参数的属性里加个注解, 注解可以自定义报错信息, 就可以实现自动非空校验 2.1 编写注解 @Target({ElementType.FIELD}) //作用的位置 @Retention(RetentionPolicy.RUNTIME) //作用域 @Documen

  • Java和C#下的参数验证方法

    参数的输入和验证问题是开发时经常遇到的,一般的验证方法如下: public bool Register(string name, int age) { if (string.IsNullOrEmpty(name)) { throw new ArgumentException("name should not be empty", "name"); } if (age < 10 || age > 70) { throw new ArgumentExcept

  • JAVA基础之注解与反射的使用方法和场景

    注解 注解定义 Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制. Java 语言中的类.方法.变量.参数和包等都可以被标注.和注释不同,Java 标注可以通过反射获取标注内容.在编译器生成类文件时,标注可以被嵌入到字节码中.Java 虚拟机可以保留标注内容,在运行 时可以获取到标注内容 . 当然它也支持自定义 Java 标注. 注解与注释的区别:注解是给机器看的注释,而注释是给程序员看的提示,编译时自动忽略注释. 使用场景 编译格式检查 反射中解

  • JAVA中通过Hibernate-Validation进行参数验证

    在开发JAVA服务器端代码时,我们会遇到对外部传来的参数合法性进行验证,而hibernate-validator提供了一些常用的参数校验注解,我们可以拿来使用. 1.maven中引入hibernate-validator对应的jar: <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>4

  • JAVA中通过自定义注解进行数据验证的方法

    前言 最近为了工作也为了更加深入了解掌握java注解的使用,决定自定义注解来实现数据验证. API开发中经常会遇到一些对请求数据进行验证的情况,这时候如果使用注解就有两个好处,一是验证逻辑和业务逻辑分离,代码清晰,二是验证逻辑可以轻松复用,只需要在要验证的地方加上注解就可以. Java提供了一些基本的验证注解,比如@NotNull.@Size,但是更多情况下需要自定义验证逻辑,这时候就可以自己实现一个验证注解,方法很简单,仅需要两个东西: 一个自定义的注解,并且指定验证器 一个验证器的实现 自定

  • Spring Boot利用JSR303实现参数验证的方法实例

    简介 JSR-303 是 JAVA EE 6 中的一项子规范,叫做 Bean Validation. 在任何时候,当你要处理一个应用程序的业务逻辑,数据校验是你必须要考虑和面对的事情.应用程序必须通过某种手段来确保输入进来的数据从语义上来讲是正确的.在通常的情况下,应用程序是分层的,不同的层由不同的开发人员来完成.很多时候同样的数据验证逻辑会出现在不同的层,这样就会导致代码冗余和一些管理的问题,比如说语义的一致性等.为了避免这样的情况发生,最好是将验证逻辑与相应的域模型进行绑定. Bean Va

  • java基于spring注解AOP的异常处理的方法

    一.前言 项目刚刚开发的时候,并没有做好充足的准备.开发到一定程度的时候才会想到还有一些问题没有解决.就比如今天我要说的一个问题:异常的处理.写程序的时候一般都会通过try...catch...finally对异常进行处理,但是我们真的能在写程序的时候处理掉所有可能发生的异常吗? 以及发生异常的时候执行什么逻辑,返回什么提示信息,跳转到什么页面,这些都是要考虑到的. 二.基于@ControllerAdvice(加强的控制器)的异常处理 @ControllerAdvice注解内部使用@Except

  • .Net Web Api中利用FluentValidate进行参数验证的方法

    前言 本文主要介绍了关于.Net Web Api用FluentValidate参数验证的相关内容,下面话不多说了,来一起看看详细的介绍吧. 方法如下 安装FluentValidate 在ASP.NET Web Api中请安装 FluentValidation.WebApi版本 创建一个需要验证的Model public class Product { public string name { get; set; } public string des { get; set; } public s

随机推荐