SpringBoot的校验及校验异常处理

SpringBoot的2.3版本之前,在web-starter中自带了校验依赖,从2.3开始需要手动添加:

<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-validation</artifactId> 
</dependency>

web层的校验

大部分情况下,校验都集中在web层进行,对controller方法的入参进行校验,代码片段如下:

@PostMapping(value = "/add")
public ResponseEntity addStu(@Valid Student student) {
    log.info("addStu student={}", student);
    return ResponseEntity.ok(student);
}

@PostMapping(value = "/update")
public ResponseEntity updateStu(@Valid @RequestBody Student student) {
    log.info("updateStu student={}", student);
    return ResponseEntity.ok(student);
}

BindException

第一个方法的入参使用了Spring的数据绑定,在@Valid入参后面还可以加上一个Errors类型或者BindingResult类型的入参,获取左边入参的校验异常信息。

但是每次这样手动收集异常信息很重复,数据绑定发生异常时,默认会抛出BindException。通常使用@RestControllerAdvice或@ControllerAdvice来进行统一异常处理。

MethodArgumentNotValidException

第二个方法,对请求体中json格式的数据转换而来的对象进行校验。如果校验失败,默认情况下会抛出MethodArgumentNotValidException异常。

同样,这样的方法也可以在后面跟上Errors或BindingResult对象。并且使用@RestControllerAdvice或@ControllerAdvice来进行统一异常处理更好。

统一的异常处理类

代码如下:

@RestControllerAdvice
@Slf4j
public class CustomExceptionHandler {

    @ExceptionHandler(Exception.class)
    public ResponseEntity handleException(Exception e){
        log.error("handleException", e);
        return ResponseEntity.ok("操作失败,"+e.getMessage());
    }
    
    @ExceptionHandler(BindException.class)
    public ResponseEntity handleBindException(BindException e) {
        log.error("handleBindException", e);
        String msg = e.getFieldErrors().stream().map(FieldError::getDefaultMessage).collect(Collectors.joining(";"));
        return ResponseEntity.ok(msg);
    }
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.error("handleMethodArgumentNotValidException", e);
        String msg = e.getBindingResult().getFieldErrors().stream().map(FieldError::getDefaultMessage).collect(Collectors.joining(";"));
        return ResponseEntity.ok(msg);
    }
    
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity handleConstraintViolationException(ConstraintViolationException e) {
        log.error("handleConstraintViolationException", e);
        String msg = e.getConstraintViolations().stream().map(ConstraintViolation::getMessage)
                .collect(Collectors.joining(";"));
        return ResponseEntity.ok(msg);
    }
}

非web层校验

在其它的地方,比如对service层的入参进行校验有时也是必要的。

发生校验的方法的对象,都是bean,并且通过AOP来完成校验。

ConstraintViolationException

bean可以直接注入一个Validator对象,然后手写代码进行校验,校验失败会抛出ConstraintViolationException。这样操作会比较繁琐,可以在统一异常处理类中处理ConstraintViolationException。

通过查看官方文档的Spring-driven Method Validation(core 3.7-Java Bean Validation章节下),为了能够进行Spring驱动的方法校验,所有的目标类都需要标记上@Validated注解。代码如下:

@Validated
@Service
public class ValidationService {

    public void print(@NotBlank(message = "参数不可为空") String msg) {
        System.out.println(msg);
    }

    /**
     * 如果想校验student内部的字段,需要加上@Valid;不加的话,则只会进行@NotNull校验。
     * @param student
     */
    public void printStu(@NotNull(message = "学生信息不可为空") Student student) {
        System.out.println(student);
    }
}

这些方法不支持web层校验的Errors和BindingResult,校验失败会抛出ConstraintViolationException。

 @RequestParam如何校验

如果入参使用了@RequestParam该如何校验呢?可以通过上述的Spring-driven Method Validation来实现。

即在Controller类上加上@Validated注解,就可以进行校验。代码如下:

@GetMapping(value = "/getStu")
public Student getStu(@RequestParam @NotBlank(message = "姓名不可为空") String name) {
    Student student = new Student();
    return student;
}

如果传入的name为空,就会抛出ConstraintViolationException。

需要注意的是对于数据绑定的入参或者@RequestBody注解的入参,处理方法没使用Errors和BindingResult,校验失败会抛出BindException;如果处理方法的入参使用了Errors或BindingResult的话,会变成抛出ConstraintViolationException。

在Controller上使用Spring-driven Method Validation,可能会让人有点混淆。所以最好不这样做,参数较少可以直接校验,较多的话使用数据绑定的对象。

@Validated和@Valid的异同

其实在数据绑定入参或者@RequestBody的入参中,@Validated和@Valid可以互相混用。但是它们还是有点区别。

@Valid支持嵌套校验而@Validated不支持,即一个类对象的字段还是一个类对象或者是集合,只需在包装类的字段上加上@Valid就可以校验被包装类。

@Validated支持分组校验,而@Valid不支持。分组校验就是在不同情况下,对同一个字段可以采取不同的校验规则。

参考连接:https://segmentfault.com/a/1190000023451809#item-7

异常相关注解@ResponseStatus

该注解用于设置状态码,还有一个可配置的属性reason,用来在错误页面上展示的文字信息。

但是该注解并不适用于REST APIs的风格。

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot提供了一套方便的校验注解,可以用于对请求参数进行校验。下面是一些常用的校验注解: 1. @NotNull:验证注解的元素值不为null。 2. @NotEmpty:验证注解的元素值不为null且不为空。 3. @NotBlank:验证注解的元素值不为null且去除首尾空格后不为空。 4. @Min:验证注解的元素值大于等于指定的最小值。 5. @Max:验证注解的元素值小于等于指定的最大值。 6. @Size:验证注解的元素值的大小在指定范围内。 7. @Pattern:验证注解的元素值符合指定的正则表达式。 8. @Email:验证注解的元素值是一个有效的电子邮件地址。 使用这些注解,可以在Controller层的请求参数上进行标记,然后在处理请求的方法中使用@Valid注解进行参数校验。如果校验失败,会抛出MethodArgumentNotValidException异常,可以通过ExceptionHandler进行统一处理。 例如,对一个User对象进行校验: ```java public class User { @NotBlank(message = "用户名不能为空") private String username; @Size(min = 6, max = 20, message = "密码长度必须在6到20之间") private String password; // 省略getter和setter } @RestController public class UserController { @PostMapping("/users") public void createUser(@Valid @RequestBody User user) { // 处理创建用户的逻辑 } // 异常处理 @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<Object> handleValidationExceptions(MethodArgumentNotValidException ex) { Map<String, String> errors = new HashMap<>(); ex.getBindingResult().getAllErrors().forEach((error) -> { String fieldName = ((FieldError) error).getField(); String errorMessage = error.getDefaultMessage(); errors.put(fieldName, errorMessage); }); return ResponseEntity.badRequest().body(errors); } } ``` 在上面的例子中,@NotBlank注解用于校验username字段不能为空,@Size注解用于校验password字段的长度必须在6到20之间。`@Valid`注解用于标记需要进行校验的参数,当校验失败时,会抛出MethodArgumentNotValidException异常,可以通过ExceptionHandler进行统一处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值