Springboot 注解


@validated

服务器和浏览器互不信任,不能因为前端加入参判断了后台就不处理了,这样是不对的。

比如前台传过来一个对象作为入参参数,这个对象中有些属性允许为空,有些属性不允许为空。那么你还在使用if()else{}进行非空判断吗?不妨尝试下使用注解,可以使用@Validated

基础使用

因为spring-boot已经引入了基础包,所以直接使用就可以了
1 首先在controller上声明需要对数据进行校验

@RestController
@RequestMapping("/api/v1")
public class Demo1Controller {
 
    @PostMapping("/insert")
    public String validatedDemo1(@Validated @RequestBody Use1Dto use1Dto){
        System.out.println(use1Dto);
        return "success";
    }
}

2 然后在bean上声明需要被校验的字段

@Data
public class User1Dto {
    /**
     * 用户名
     */
    @NotBlank(message = "用户名不能为空!")
    private String username;
    /**
     * 性别
     */
    @NotBlank(message = "性别不能为空!")
    private String gender;
    /**
     * 年龄
     */
    @Min(value = 1, message = "年龄有误!")
    @Max(value = 120, message = "年龄有误!")
    private int age;
    /**
     * 地址
     */
    @NotBlank(message = "地址不能为空!")
    private String address;
    /**
     * 邮箱
     */
    @Email(message = "邮箱有误!")
    private String email;
    /**
     * 手机号码
     */
    @Pattern(regexp = "^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\\d{8}$",message = "手机号码有误!")
    private String mobile;
}

而后,当输入不能满足条件是,就会抛出异常,而后统一由异常中心处理
也可以用BindingResult,但是用了这个后就必须手动处理异常,侵入了正常的逻辑过程,并不推荐.

说明:若不做异常处理,@Validated注解的默认异常消息如下(示例):

2020-09-05 21:48:38.106  WARN 9796 --- [nio-8080-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public java.lang.String com.example.validateddemo.controller.DemoController.validatedDemo1(com.example.validateddemo.entity.dto.UseDto): [Field error in object 'useDto' on field 'username': rejected value [null]; codes [NotBlank.useDto.username,NotBlank.username,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [useDto.username,username]; arguments []; default message [username]]; default message [用户名不能为空!]] ]

常用注解类型

注意,不要错用了异常类型,比如在int上不可用@size

数值检查,建议使用在StirngInteger类型,不建议使用在int类型上,因为表单值为""时无法转换为int,但可以转换为Stirng"",Integernull

嵌套校验

@Validated或者@Valid区别

  1. @Validated:可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上
  2. @Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上
  3. 两者是否能用于成员属性(字段)上直接影响能否提供嵌套验证的功能。

未嵌套情况

比如我们现在有个实体叫做Item

public class Item {

    @NotNull(message = "id不能为空")
    @Min(value = 1, message = "id必须为正整数")
    private Long id;

    @NotNull(message = "props不能为空")
    @Size(min = 1, message = "至少要有一个属性")
    private List props;
}

Item带有很多属性,属性里面有:pidvidpidNamevidName,如下所示:

public class Prop {

    @NotNull(message = "pid不能为空")
    @Min(value = 1, message = "pid必须为正整数")
    private Long pid;

    @NotNull(message = "vid不能为空")
    @Min(value = 1, message = "vid必须为正整数")
    private Long vid;

    @NotBlank(message = "pidName不能为空")
    private String pidName;

    @NotBlank(message = "vidName不能为空")
    private String vidName;
}

属性这个实体也有自己的验证机制,比如pidvid不能为空,pidNamevidName不能为空等。
现在我们有个ItemController接受一个Item的入参,想要对Item进行验证,如下所示:

@RestController
public class ItemController {

    @RequestMapping("/item/add")
    public void addItem(@Validated Item item) {
        doSomething();
    }
}

在上图中,如果Item实体的props属性不额外加注释,只有@NotNull@Size,无论入参采用@Validated还是@Valid验证,Spring Validation只会对Itemidprops做非空和数量验证,不会对props字段里的Prop实体进行字段验证,也就是@Validated@Valid加在方法参数前,都不会自动对参数进行嵌套验证。也就是说如果传的List中有Proppid为空或者是负数,入参验证不会检测出来。

嵌套情况

为了能够进行嵌套验证,必须手动在Item实体的props字段上明确指出这个字段里面的实体也要进行验证。由于@Validated不能用在成员属性(字段)上,但是@Valid能加在成员属性(字段)上,而且@Valid类注解上也说明了它支持嵌套验证功能,那么我们能够推断出:@Valid加在方法参数时并不能够自动进行嵌套验证,而是用在需要嵌套验证类的相应字段上,来配合方法参数上@Validated@Valid来进行嵌套验证。

修改Item类如下所示:

public class Item {

    @NotNull(message = "id不能为空")
    @Min(value = 1, message = "id必须为正整数")
    private Long id;

    @Valid // 嵌套验证必须用@Valid
    @NotNull(message = "props不能为空")
    @Size(min = 1, message = "props至少要有一个自定义属性")
    private List props;
}

然后我们在ItemControlleraddItem函数上再使用@Validated或者@Valid,就能对Item的入参进行嵌套验证。此时Item里面的props如果含有Prop的相应字段为空的情况,Spring Validation框架就会检测出来,记录相应的错误。

@Validated和@Valid在嵌套验证功能上的区别

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

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

BindingResult

    @PostMapping("/a")
    @ApiOperation(value = "测试", notes = "")
    public void test(@RequestBody @Valid TestEntity test,BindingResult bindingResult) {
        System.out.println(test.toString());
        if (bindingResult.hasErrors()) {
            throw new 自定义Exception("错误提示码",bindingResult.getFieldError().getDefaultMessage());
        }
    }

bindingResult.hasErrors() 判断是否校验通过,未通过bindingResult.getFieldError().getDefaultMessage()获取在TestEntity的属性设置的自定义message,如果没有设置,则返回默认值javax.validation.constraints.XXX.message

    @RequestMapping("/b")
    @ResponseBody
    public Response runBydomain(@RequestBody @Validated TaskDomainVO taskDomainVO, BindingResult bindingResult) {
        log.info("Execution code analyze,", taskDomainVO.toString());
        if (bindingResult.hasErrors()) {
            String errorCode = bindingResult.getAllErrors().get(0).getDefaultMessage();
            log.info("Execution failed, errorCode:{}", errorCode);
            return new Response(ResultCode.GLOBAL_PARAM_ERROR, errorCode);
        }