优雅的参数校验@Validated 实战 + 统一异常处理返回前端json 最全解析

数据的校验的重要性就不用说了,即使在前端对数据进行校验的情况下,我们还是要对传入后端的数据再进行一遍校验,使用@Valid和@Validated注解可以很好的避免后端代码满屏if-else。

1 引入依赖

springboot版本小于2.3时spring-boot-starter-web包含了依赖不用再引入

当springboot版本大于2.3时需要手动引入依赖

  org.springframework.boot spring-boot-starter    javax.validation validation-api 2.0.1.Final    org.hibernate.validator hibernate-validator 

2 @Valid和@Validated的用法(区别)

二者主要作用在于 都作为标准JSR-303规范,在检验Controller的入参是否符合规范时,使用@Validated或者@Valid在基本验证功能上没有太多区别。但是在分组、注解地方、嵌套验证等功能上两个有所不同:

@Valid注解用于校验,所属包为:javax.validation.Valid。

用在方法入参上无法单独提供嵌套验证功能。**能够用在成员属性(字段)**上,提示验证框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。

@Validated是@Valid 的一次封装,是Spring提供的校验机制使用。

用在方法入参上无法单独提供嵌套验证功能。不能用在成员属性(字段)上,也无法提示框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。

3 常用校验注解

@NotEmpty 被注释的字符串的不能为 null 也不能为空

@NotBlank 被注释的字符串非 null,并且必须包含一个非空白字符

@Null 被注释的元素(数字)必须为 null

@NotNull 被注释的元素(数字)必须不为 null

@AssertTrue 被注释的元素必须为 true

@AssertFalse 被注释的元素必须为 false

@Pattern(regex=,flag=)被注释的元素必须符合指定的正则表达式

@Email 被注释的元素必须是 Email 格式。

@Min(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值

@Max(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值

@DecimalMin(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值

@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值

@Size(max=, min=) 被注释的元素(集合)的大小必须在指定的范围内

@Digits (integer, fraction)被注释的元素必须是一个数字,其值必须在可接受的范围内

@Past被注释的元素必须是一个过去的日期

@Future 被注释的元素必须是一个将来的日期

@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内

@Range(min=,max=,message=) 被注释的元素必须在合适的范围内

4 参数校验

4.1 控制器上加校验注解

千万不要忘记在类上加@Validated注解,否则验证注解不生效

@RestController
@Api("阅读记录")
@RequestMapping("/record")
@Validated
public class ReadRecordController { @Autowired
    private ReadRecordService readRecordService;
    @PostMapping("/addRecord")
    @ApiOperation("新增阅读记录")
    public ResponseResult addRecord(@RequestBody @Valid ReadRecord readRecord){ return readRecordService.addRecord(readRecord);
    }
    @ApiOperation("查询阅读记录")
    @GetMapping("/getReadRecord")
    public ResponseResult getReadRecord(@Valid @NotBlank(message = "fileCode不能传空值") String fileCode){ return readRecordService.getReadRecord(fileCode);
    }
}

4.2 实体类上加校验注解

对接受实体加注解的字段进行校验,如果验证失败,它将抛出MethodArgumentNotValidException异常。

@Data
public class ReadRecord { private String id;
    private String companyName;
    private String deleteFlag;
    @NotBlank(message = "loginName不能传空")
    private String loginName;
    @NotBlank(message = "fileCode不能传空")
    private String fileCode;
    }

5 自定义注解

如果在写项目的过程中,参数需要的条件注解满足不上,则我们需要自定义注解来完成

5.1 创建一个自定义的注解类

@Documented
@Constraint(validatedBy = PhoneNumberValidator.class)
@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
public@interface PhoneNumber { String message() default "Invalid phone number";
    Class[] groups() default {};
    Class[] payload() default {};
}

5.2 创建一个逻辑处理数据的方法

publicclass PhoneNumberValidator implements ConstraintValidator { @Override
    public boolean isValid(String phoneField, ConstraintValidatorContext context) { if (phoneField == null) { // can be null
            returntrue;
        }
        return phoneField.matches("^1(3[0-9]|4[57]|5[0-35-9]|8[0-9]|70)\\d{8}$") && phoneField.length() > 8 && phoneField.length() < 14;
    }
}

5.3使用自定义注解

@PhoneNumber(message = "phoneNumber 格式不正确")
@NotNull(message = "phoneNumber 不能为空")
private String phoneNumber;

6 统一异常处理

我们使用了@valiated注解,也写了@NotBlank校验,控制台也打印了校验信息,但是前端就是没有收到我们返回的校验信息.是因为我们没有自定义捕获异常后的处理方式,没有返回给前端这些处理信息。

如果要将报错信息返回给前端只需要对信息做统一异常处理就好。

@ControllerAdvice
public class CustomHandle { @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public ResponseResult methodArgumentNotValidException(MethodArgumentNotValidException e){ List allErrors = e.getAllErrors();
        List collect = allErrors.stream()
                .map(error -> error.getDefaultMessage())
                .collect(Collectors.toList());
        return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,collect.toString());
    }
}

至此便可将报错信息返回前端

{"token": null,
	"code": 501,
	"message": "[loginName不能传空]",
	"data": null,
	"status": null
}

如果觉得文章对您有用,还请一键三连哦!!!更多实用文章在主页!