SpringBoot使用validation-api实现参数校验

时间:2020-9-7 作者:admin


SpringBoot使用validation-api实现参数校验

前言

我们在开发Java项目的时候,经常需要对参数进行一些必填项、格式、长度等进行校验,如果手写代码对参数校验,每个接口会需要很多低级的代码,这样会降低代码的可读性。那么我们能不能使用一种比较优雅的方式来实现,对请求中的参数进行校验呢?

knife4j的安装与使用可参考我的博客:SpringBoot使用knife4j进行在线接口调试

正文

ValidationApi框架就是用来解决参数校验中代码冗余问题,ValidationApi框架提供一些注解用来帮助我们对请求参数进行校验:

SpringBoot使用validation-api实现参数校验

SpringBoot使用validation-api实现参数校验

注入依赖

<!--参数校验-->
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>

<!--提供一些字符串操作-->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.3.2</version>
</dependency>

<!--lombok-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.2</version>
    <optional>true</optional>
</dependency>

<!--knife4j接口-->
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>2.0.4</version>
</dependency>

UserPojoReq.java请求封装类

  • 如果成员变量是其他对象实体,该变量必须加 @Valid,否则嵌套中的验证不生效
@Setter
@Getter
@ToString
@ApiModel("用户对象")
public class UserPojoReq extends Request implements Serializable {


    private static final long serialVersionUID = -354657839724457905L;

    @ApiModelProperty(required = true, notes = "主键", example = "123")
    private String id;

    @ApiModelProperty(required = true, notes = "用户名", example = "luo")
    @NotNull(message = "用户姓名为必填项,不得为空")
    @Size(min = 2,max = 20,message = "用户名长度要在2—8个字符")
    private String name;

    @ApiModelProperty(required = true, notes = "消息", example = "消息")
    private String msg;


}

CouponTypeEnum.class :错误码枚举类

@Getter
public enum CouponTypeEnum {

    PARAMETER_ERROR(1001, "请求参数有误!"),
    UNKNOWN_ERROR(9999, "未知的错误!);

    /**
     * 状态值
     */
    private int couponType;


    /**
     * 状态描述
     */
    private String couponTypeDesc;

    CouponTypeEnum(int couponType, String couponTypeDesc){
        this.couponType = couponType;
        this.couponTypeDesc = couponTypeDesc;
    }

    public static String getDescByType(int couponType) {
        for (CouponTypeEnum type : CouponTypeEnum.values()) {
            if (type.couponType == couponType) {
                return type.couponTypeDesc;
            }
        }
        return null;
    }


    public String getcouponTypeStr(){
        return String.valueOf(this.couponType);
    }


}

BusinessException.java:自定义业务异常类

/**
 * 业务自定义异常
 */
@Getter
public class BusinessException extends RuntimeException {

    private static final long serialVersionUID = -1895174013651345407L;
    private final CouponTypeEnum errorCode;
    private String primaryErrorCode;
    private String primaryErrorMsg;
    private String primaryErrorIP;

    public BusinessException(CouponTypeEnum errorCode) {
        this(errorCode, errorCode.getCouponTypeDesc());
    }
    public BusinessException(CouponTypeEnum errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }
    public BusinessException(CouponTypeEnum errorCode, String message,String primaryErrorCode,String primaryErrorMsg,String primaryErrorIP) {
        super(message);
        this.errorCode = errorCode;
        this.primaryErrorCode=primaryErrorCode;
        this.primaryErrorMsg=primaryErrorMsg;
        this.primaryErrorIP=primaryErrorIP;
    }
    public BusinessException(CouponTypeEnum errorCode,String primaryErrorCode,String primaryErrorMsg,String primaryErrorIP) {
        this(errorCode, errorCode.getCouponTypeDesc());
        this.primaryErrorCode=primaryErrorCode;
        this.primaryErrorMsg=primaryErrorMsg;
        this.primaryErrorIP=primaryErrorIP;
    }

}

GlobalExceptionHandler.class 拦截异常并统一处理

  • MissingServletRequestParameterException:必填项为null异常
  • HttpMessageNotReadableException:参数类型不匹配异常
  • MethodArgumentNotValidException:JSON校验失败异常(比如长度等)
  • BusinessException:自定义的异常
  • Exception:其他异常
@RestControllerAdvice("com.luo.producer.controller")
@Slf4j
public class GlobalExceptionHandler {
    
    
    /**
     * 忽略参数异常处理器
     *
     * @param e 忽略参数异常
     * @return Response
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Response parameterMissingExceptionHandler(MissingServletRequestParameterException e) {
        log.error("", e);
        return new Response(CouponTypeEnum.PARAMETER_ERROR.getcouponTypeStr(), "请求参数 " + e.getParameterName() + " 不能为空");
    }


    /**
     * 缺少请求体异常处理器
     *
     * @param e 缺少请求体异常
     * @return Response
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Response parameterBodyMissingExceptionHandler(HttpMessageNotReadableException e) {
        log.error("", e);
        return new Response(CouponTypeEnum.PARAMETER_ERROR.getcouponTypeStr(), "参数体不能为空");
    }


    /**
     * 参数效验异常处理器
     *
     * @param e 参数验证异常
     * @return ResponseInfo
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Response parameterExceptionHandler(MethodArgumentNotValidException e) {
        log.error("", e);
        // 获取异常信息
        BindingResult exceptions = e.getBindingResult();
        // 判断异常中是否有错误信息,如果存在就使用异常中的消息,否则使用默认消息
        if (exceptions.hasErrors()) {
            List<ObjectError> errors = exceptions.getAllErrors();
            if (!errors.isEmpty()) {
                // 这里列出了全部错误参数,按正常逻辑,只需要第一条错误即可
                FieldError fieldError = (FieldError) errors.get(0);
                return new Response(CouponTypeEnum.PARAMETER_ERROR.getcouponTypeStr(), fieldError.getDefaultMessage());
            }
        }
        return new Response(CouponTypeEnum.PARAMETER_ERROR);
    }


    /**
     * 自定义参数错误异常处理器
     *
     * @param e 自定义参数
     * @return ResponseInfo
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({BusinessException.class})
    public Response paramExceptionHandler(BusinessException e) {
        log.error("", e);
        // 判断异常中是否有错误信息,如果存在就使用异常中的消息,否则使用默认消息
        if (!StringUtils.isEmpty(e.getMessage())) {
            return new Response(CouponTypeEnum.PARAMETER_ERROR.getcouponTypeStr(), e.getMessage());
        }
        return new Response(CouponTypeEnum.PARAMETER_ERROR);
    }


    /**
     * 其他异常
     *
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({Exception.class})
    public Response otherExceptionHandler(Exception e) {
        log.error("其他异常", e);
        // 判断异常中是否有错误信息,如果存在就使用异常中的消息,否则使用默认消息
        if (!StringUtils.isEmpty(e.getMessage())) {
            return new Response(CouponTypeEnum.UNKNOWN_ERROR.getcouponTypeStr(), e.getMessage());
        }
        return new Response(CouponTypeEnum.UNKNOWN_ERROR);
    }
}

验证

测试接口

  • @Valid被标记的实体将会开启一个校验的功能
  • @RequestBody:请求实体需要加上@RequestBody否则MethodArgumentNotValidException异常将会被识别成Exception异常,提示信息将与预期不符。
@RestController
@Slf4j
public class UserController {

    @PostMapping("/helloluo")
    @MyPermissionTag(value = "true")
    public String helloluo(@RequestBody @Valid UserPojoReq userPojoReq){
        return "Hello World”+userPojoReq;
    }
}

模拟请求参数,进行接口访问:

SpringBoot使用validation-api实现参数校验

SpringBoot使用validation-api实现参数校验

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。