掘金 后端 ( ) • 2024-04-23 16:51

theme: channing-cyan

在日常开发中,我们有时候需要对一些参数进行校验,此时需要书写大量的逻辑判断,会显得代码比较冗余(如图所示)。

校验.png 此时可以使用如下注解方式来简化代码。

Valid注解方式

引入相关依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
    <version>3.2.1</version>
</dependency>
@Data
public class ModifyNameReq {
    @NotBlank
    @Size(max = 6,message = "名称不能太长哦!")
    private String name;
    @NotBlank
    @Size(max = 6,message = "名称不能太长哦!")
    private String address;
    @NotBlank
    private String img;
}

@Valid要加上,否则会没有效果,校验不了

@RestController
public class EurekaServerController {
    @PostMapping("test")
    public String save(@Valid @RequestBody ModifyNameReq nameReq){
        return "OK";
    }
}

使用postman进行测试

validation-test.png

此时如果不配置异常处理器,会出现如下错误。所以为了更好的显示和更友好的提醒用户可以配置异常处理器。

validation-error.png

异常处理器

@RestControllerAdvice  // 监听打了@RestController注解的Controller
public class GlobalExceptionHandler {
    /**
     * validation参数校验异常
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class) // 监听该类型的错误
    public ApiResult<?> methodArgumentNotValidException(MethodArgumentNotValidException exception){
        StringBuilder errorMsg = new StringBuilder();
        exception.getBindingResult().getFieldErrors() // 获取所有错误字段信息
                .forEach(item->errorMsg.append(item.getField()).append(item.getDefaultMessage()));
        return ApiResult.fail(CommonErrorEnum.PARAM_VALID.getCode(),errorMsg.substring(0,errorMsg.length() -1));
    }
}

上述代码中,大家只需要了解如何获取错误字段信息即可,前面的ApiResult只是用来封装返回给前端结果的工具类

此时有的小伙伴就又会说了,那我如果是对象里面包含了一个对象,也需要进行数据的校验怎么办呢?此时我们就可以采取下面的方式了。

@Data
public class ModifyNameReq {
    @NotBlank
    @Size(max = 6,message = "名称不能太长哦!")
    private String name;
    @NotBlank
    private String img;
    @Valid // 这个注解会触发Address对象的校验  
    private Address address;
}
@Data
public class Address {
    @NotBlank(message = "城市不能为空")
    @Size(max = 50, message = "城市名称不能超过50个字符")
    private String city;  
      
    @NotBlank(message = "街道不能为空")  
    @Size(max = 100, message = "街道名称不能超过100个字符")  
    private String street;  
}

validation-postman.png

此时我们并没有输入street字段,此时看看会出现什么错误呢?

validation-error.png

大家可以很清晰地看见我们的嵌套的校验生效了。

此时又有小伙伴们说了,那么如果对象里面包含一个集合对象呢

@Data
public class ModifyNameReq {
    @NotBlank
    @Size(max = 6,message = "名称不能太长哦!")
    private String name;
    @NotBlank
    private String img;
    @Valid // 这个注解会触发Address对象的校验  
    private List<Address> address;
}

validation-error.png

此时发送请求,出现的异常会跟上述异常相同。

非注解方式

在上面的示例中,ModifyNameReq类包含了一个Address类型的属性和一个Address类型的列表属性。对于这两个属性,我们都使用了@Valid注解来指示需要对它们进行校验。此外,我们还为citystreetname等属性添加了其他的校验注解,如@NotNull@Size

要使用这些校验注解,还需要配置一个校验器(Validator),并在需要的时候调用它来执行校验。这通常是通过Spring框架自动完成的,但是也可以手动创建和使用Validator实例。以下是一个简单的示例,展示了如何手动使用Validator来校验ModifyNameReq对象:

import javax.validation.ConstraintViolation;
import javax.validation.Validation;  
import javax.validation.Validator;  
import javax.validation.ValidatorFactory;
public class ValidationExample {  
  
    public static void main(String[] args) {  
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();  
        Validator validator = factory.getValidator();

        ModifyNameReq modifyNameReq = new ModifyNameReq();
        modifyNameReq.setImg("http:132.png");
        modifyNameReq.setName("手动创建使用Validator实例");
        Address address = new Address();
        address.setCity("北京");
        modifyNameReq.setAddress(Arrays.asList(address));
        Set<ConstraintViolation<ModifyNameReq>> violations = validator.validate(modifyNameReq);
        if (!violations.isEmpty()) {  
            for (ConstraintViolation<ModifyNameReq> violation : violations) {
                System.out.println(violation.getMessage());  
            }  
        } else {  
            System.out.println("Validation succeeded");  
        }  
    }  
}

此时大家可以运行这段代码,看看会有什么信息打印出来吧。