Spring Boot参数校验以及分组校验的使用

简介:作web开发基本上每一个接口都要对参数进行校验,若是参数比较少,还比较容易处理,一但参数比较多了的话代码中就会出现大量的if-else语句。虽然这种方式简单直接,但会大大下降开发效率和代码可读性。因此咱们可使用validator组件来代替咱们进行没必要要的coding操做。本文将基于validator的介绍资料,同时结合做者本身在项目中的实际使用经验进行了总结。

 title=

做者 | 江岩
来源 | 阿里技术公众号前端

一 前言

作web开发有一点很烦人就是要对前端输入参数进行校验,基本上每一个接口都要对参数进行校验,好比一些非空校验、格式校验等。若是参数比较少的话仍是容易处理的一但参数比较多了的话代码中就会出现大量的if-else语句。java

使用这种方式虽然简单直接,可是也有很差的地方,一是下降了开发效率,由于咱们须要校验的参数会存在不少地方,而且不一样地方会有重复校验,其次下降了代码可读性,由于在业务代码中掺杂了太多额外工做的代码。web

因此咱们可使用validator组件来代替咱们进行没必要要的coding操做。本文基于validator的介绍资料,也结合本身在项目中的实际使用经验进行了总结,但愿能帮到你们。spring

1 什么是validator

Bean Validation是Java定义的一套基于注解的数据校验规范,目前已经从JSR 303的1.0版本升级到JSR 349的1.1版本,再到JSR 380的2.0版本(2.0完成于2017.08),已经经历了三个版本 。须要注意的是,JSR只是一项标准,它规定了一些校验注解的规范,但没有实现,好比@Null、@NotNull、@Pattern等,它们位于 javax.validation.constraints这个包下。而hibernate validator是对这个规范的实现,并增长了一些其余校验注解,如 @NotBlank、@NotEmpty、@Length等,它们位于org.hibernate.validator.constraints这个包下。segmentfault

若是咱们的项目使用了Spring Boot,hibernate validator框架已经集成在 spring-boot-starter-web中,因此无需再添加其余依赖。若是不是Spring Boot项目,须要添加以下依赖。app

 title=

二 注解介绍

1 validator内置注解

 title=

hibernate validator中扩展定义了以下注解:框架

 title=

三 使用

使用起来比较简单,都是使用注解方式使用。具体来讲分为单参数校验、对象参数校验,单参数校验就是controller接口按照单参数接收前端传值,没有封装对象进行接收,若是有封装对象那就是对象参数校验。spring-boot

1 单参数校验

单参数校验只须要在参数前添加注解便可,以下所示:post

public Result deleteUser(@NotNull(message = "id不能为空") Long id) {
  // do something
}

但有一点须要注意,若是使用单参数校验,controller类上必须添加@Validated注解,以下所示:大数据

@RestController
@RequestMapping("/user")
@Validated // 单参数校验须要加的注解
public class UserController {
  // do something
}

2 对象参数校验

对象参数校验使用时,须要先在对象的校验属性上添加注解,而后在Controller方法的对象参数前添加@Validated 注解,以下所示:

public Result addUser(@Validated UserAO userAo) {
    // do something
}

public class UserAO {
  @NotBlank
  private String name;

  @NotNull
  private Integer age;
  
  ……
}

注解分组

在对象参数校验场景下,有一种特殊场景,同一个参数对象在不一样的场景下有不一样的校验规则。好比,在建立对象时不须要传入id字段(id字段是主键,由系统生成,不禁用户指定),可是在修改对象时就必需要传入id字段。在这样的场景下就须要对注解进行分组。

1)组件有个默认分组Default.class, 因此咱们能够再建立一个分组UpdateAction.class,以下所示:

public interface UpdateAction {
}

2)在参数类中须要校验的属性上,在注解中添加groups属性:

public class UserAO {

    @NotNull(groups = UpdateAction.class, message = "id不能为空")
    private Long id;
    
    @NotBlank
    private String name;

    @NotNull
    private Integer age;
    
    ……
}

如上所示,就表示只在UpdateAction分组下校验id字段,在默认状况下就会校验name字段和age字段。

而后在controller的方法中,在@Validated注解里指定哪一种场景便可,没有指定就表明采用Default.class,采用其余分组就须要显示指定。以下代码便表示在addUser()接口中按照默认状况进行参数校验,在updateUser()接口中按照默认状况和UpdateAction分组对参数进行共同校验。

public Result addUser(@Validated UserAO userAo) {
  // do something
}
public Result updateUser(@Validated({Default.class, UpdateAction.class}) UserAO userAo) {
  // do something
}

对象嵌套

若是须要校验的参数对象中还嵌套有一个对象属性,而该嵌套的对象属性也须要校验,那么就须要在该对象属性上增长@Valid注解。

public class UserAO {

    @NotNull(groups = UpdateAction.class, message = "id不能为空")
    private Long id;
    
    @NotBlank
    private String name;

    @NotNull
    private Integer age;
    
    @Valid
    private Phone phone;
    
    ……
}

public class Phone {
    @NotBlank
    private String operatorType;
    
    @NotBlank
    private String phoneNum;
}

3 错误消息的捕获

参数校验失败后会抛出异常,咱们只须要在全局异常处理类中捕获参数校验的失败异常,而后将错误消息添加到返回值中便可。捕获异常的方法以下所示,返回值Result是咱们系统自定义的返回值类。

@RestControllerAdvice(basePackages= {"com.alibaba.dc.controller","com.alibaba.dc.service"})
public class GlobalExceptionHandler {

  @ExceptionHandler(value = {Throwable.class})
  Result handleException(Throwable e, HttpServletRequest request){
    // 异常处理
        }
}

须要注意的是,若是缺乏参数抛出的异常是MissingServletRequestParameterException,单参数校验失败后抛出的异常是ConstraintViolationException,get请求的对象参数校验失败后抛出的异常是BindException,post请求的对象参数校验失败后抛出的异常是MethodArgumentNotValidException,不一样异常对象的结构不一样,对异常消息的提取方式也就不一样。以下图所示:

1)MissingServletRequestParameterException

if(e instanceof MissingServletRequestParameterException){
    Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL);
    String msg = MessageFormat.format("缺乏参数{0}", ((MissingServletRequestParameterException) e).getParameterName());
    result.setMessage(msg);
    return result;
}

2)ConstraintViolationException异常

if(e instanceof ConstraintViolationException){
  // 单个参数校验异常
  Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL);
  Set<ConstraintViolation<?>> sets = ((ConstraintViolationException) e).getConstraintViolations();
  if(CollectionUtils.isNotEmpty(sets)){
    StringBuilder sb = new StringBuilder();
    sets.forEach(error -> {
                    if (error instanceof FieldError) {
                        sb.append(((FieldError)error).getField()).append(":");
                    }
                    sb.append(error.getMessage()).append(";");
                });
    String msg = sb.toString();
    msg = StringUtils.substring(msg, 0, msg.length() -1);
    result.setMessage(msg);
  }
  return result;
}

3)BindException异常

if (e instanceof BindException){
      // get请求的对象参数校验异常
      Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL);
      List<ObjectError> errors = ((BindException) e).getBindingResult().getAllErrors();
      String msg = getValidExceptionMsg(errors);
      if (StringUtils.isNotBlank(msg)){
        result.setMessage(msg);
      }
      return result;
}
private String getValidExceptionMsg(List<ObjectError> errors) {
  if(CollectionUtils.isNotEmpty(errors)){
    StringBuilder sb = new StringBuilder();
    errors.forEach(error -> {
                    if (error instanceof FieldError) {
                       sb.append(((FieldError)error).getField()).append(":");
                    }
                    sb.append(error.getDefaultMessage()).append(";");
                });
    String msg = sb.toString();
    msg = StringUtils.substring(msg, 0, msg.length() -1);
    return msg;
  }
  return null;
}

4)MethodArgumentNotValidException异常

if (e instanceof MethodArgumentNotValidException){
      // post请求的对象参数校验异常
      Result result = Result.buildErrorResult(ErrorCodeEnum.PARAM_ILLEGAL);
      List<ObjectError> errors = ((MethodArgumentNotValidException) e).getBindingResult().getAllErrors();
      String msg = getValidExceptionMsg(errors);
      if (StringUtils.isNotBlank(msg)){
        result.setMessage(msg);
      }
      return result;
}

《一站式大数据开发治理DataWorks使用宝典》官方电子书开放下载

DataWorks官方入门电子书出版啦,零基础入门大数据开发治理,全面了解DataWorks十大功能模块,快速上手DataWorks核心功能。

点击这里,便可下载~

本文内容由阿里云实名注册用户自发贡献,版权归原做者全部,阿里云开发者社区不拥有其著做权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。若是您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将马上删除涉嫌侵权内容。
相关文章
相关标签/搜索