@NotEmpty、@NotBlank等注解的正确使用 @Validated和@Valid的区别 解决@NotBlank等注解不生效的问题 使用BindingResult进行接口请求参数的统一校验

2023年2月19日07:55:45

1. 问题说明

服务端通常将controller层作为调用的第一层,因而参数校验常常在这里完成,比如非空校验、类型校验等,如下登录接口代码所示:

 /**
   * 登录接口
   *
   * @author 念兮为美
   * @datetime 2022/8/11:13:41
   */
  @PostMapping("/login")
  public JSONObject login(@RequestBody UserLoginDto userLogin) {
    String password = userLogin.getPassword();
    JSONObject jsonObject=new JSONObject();
    if (null == password || "".equals(password)) {
      jsonObject.put("success",false);
      jsonObject.put("message","密码不能为空");
      return  jsonObject;
    }
    ...
  }

假如在controller层的某个方法中,有很多个需要校验的请求参数,这样写无疑会有多条if判断语句,因而,我们需要使用优雅的方式处理接口请求参数。

2. 配置依赖

前提是需要在pom文件中增加如下依赖:

<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-api</artifactId>
    <version>8.0.1</version>
</dependency>

3. 编写注解的bean

依赖配置好后,编写需要注解的bean,如下代码所示:

package com.superjson.superjsonmanager.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;


/**
 * @author 念兮为美
 * @datetime 2022/8/5 21:34
 * @desc 用户登录请求参数
 */
@AllArgsConstructor
@NoArgsConstructor
@Data
public class UserLoginDto {
  /** 密码 */
  @NotBlank(message = "密码不能为空")
  private String password;

  /** 类型 */
  @NotBlank(message = "登录类型不能为空")
  private String type;

  /** 用户名 */
  @NotBlank(message = "账号不能为空")
  private String username;
}

4. 常用注解说明

我们常用的注解有@NotBlank 、@NotEmpty、@NotNull 等,当然,我们也有可能用到其他注解来检查属性。

4.1 空检查

注解 说明
@Null 检查对象是否为null。
@NotNull 检查对象是否不为null,无法查检长度为0的字符串。
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格。
@NotEmpty 检查约束元素是否为NULL或者是EMPTY。

4.2 布尔检查

注解 说明
@AssertTrue 检查 Boolean 对象是否为 true。
@AssertFalse 检查 Boolean 对象是否为 false。

4.3 长度检查

注解 说明
@Size(min=, max=) 检查对象(Array,Collection,Map,String)长度是否在给定的范围之内 。
@Length(min=, max=) 检查字符串(String)长度是否在给定的范围之内。

4.4 日期检查

注解 说明
@Past 检查DateCalendar对象是否在当前时间之前 。
@Future 检查DateCalendar对象是否在当前时间之后 。
@Pattern 检查String对象是否符合正则表达式的规则。

4.5 数值检查

建议在Stirng和Integer类型使用,不建议在int类型上使用。因为表单值为“”时无法转换为int,但可以转换为Stirng为"",Integer为null。

注解 说明
@Min 检查 Number 和 String 对象是否大等于指定的值 。
@Max 检查 Number 和 String 对象是否小等于指定的值 。
@DecimalMax 被标注的值必须不大于约束中指定的最大值。 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度。
@DecimalMin 被标注的值必须不小于约束中指定的最小值.。这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度。
@Digits 检查 Number 和 String 的构成是否合法 。
@Digits(integer=,fraction=) 检查字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。
@Range(min=, max=) 检查数字是否介于min和max之间。

5. 编写controller

我们在bean中通过注解定义好类之后,需要在controller层加上@ValidatedBindingResult参数,如下代码所示:


package com.superjson.superjsonmanager.controller;

import com.alibaba.fastjson.JSONObject;
import com.superjson.superjsonmanager.dto.UserLoginDto;
import com.superjson.superjsonmanager.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 念兮为美
 * @datetime 2022/8/5 21:30
 * @desc 用户登录控制器
 */
@RestController
@RequestMapping("/user")
public class UserController {

  @Autowired private UserService userService;

  @PostMapping("/login")
  public JSONObject login(
      @Validated @RequestBody UserLoginDto userLogin, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
      JSONObject jsonObject = new JSONObject();
      FieldError fieldError = bindingResult.getFieldError();
      //获取出错的属性
      jsonObject.put("field", fieldError.getField());
      //获取出错的注解名字,比如notBlank,notNull等
      jsonObject.put("code", fieldError.getCode());
      //获取出错的对象名称
      jsonObject.put("objectName", fieldError.getObjectName());
      //获取错误的信息
      jsonObject.put("message", fieldError.getDefaultMessage());
      throw new 你自定义的异常("参数请求异常:" + fieldError.getDefaultMessage());
    }
    return userService.getByUsernameAndPassword(
            userLogin.getUsername(), userLogin.getPassword(), userLogin.getType());
  }
}

5.1 @Validated和@Valid区别

  1. @Valid 和 @Validated 两者都可以对数据进行校验,待校验字段上打的规则注解(@NotNull, @NotEmpty等)都可以对 @Valid 和 @Validated 生效;

  2. @Valid 进行校验的时候,需要用 BindingResult 来做一个校验结果接收。当校验不通过的时候,如果手动不 return ,则并不会阻止程序的执行;

  3. @Validated 进行校验的时候,当校验不通过的时候,程序会抛出400异常,阻止方法中的代码执行,这时需要再写一个全局校验异常捕获处理类,然后返回校验提示。

  4. 总体来说,@Validated 使用起来要比 @Valid 方便一些,它可以帮我们节省一定的代码,并且使得方法看上去更加的简洁。

6. 解决@NotBlank等注解不生效的问题

@NotEmpty、@NotBlank等注解的正确使用 @Validated和@Valid的区别 解决@NotBlank等注解不生效的问题 使用BindingResult进行接口请求参数的统一校验

在如上postman中,password和username都为空,按道理说,应该是报错的,但执行后并没有报错,如下图所示:

@NotEmpty、@NotBlank等注解的正确使用 @Validated和@Valid的区别 解决@NotBlank等注解不生效的问题 使用BindingResult进行接口请求参数的统一校验

根据上述3. 编写需要注解的bean可以看到,password和username都不为空,实际上应该有两个error,但现在是0 error,就不正常。

实际上,我们少引用hibernate-validator这个依赖,只需要增加如下依赖即可:

<dependency>
  <groupId>org.hibernate.validator</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>6.2.3.Final</version>
</dependency>

增加如下依赖之后,重启服务后运行。不仅能让@NotBlank注解生效,也可以让@NotBlank等注解生效,如下图所示:

@NotEmpty、@NotBlank等注解的正确使用 @Validated和@Valid的区别 解决@NotBlank等注解不生效的问题 使用BindingResult进行接口请求参数的统一校验

7. 其他注解说明

注解 说明
@Valid 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验.(是否进行递归验证)。
@CreditCardNumber 信用卡验证。
@Email 验证是否是邮件地址,如果为null,不进行验证,算通过验证。
@ScriptAssert(lang= ,script=, alias=) 检查脚本语言是否符合预期
@URL(protocol=,host=, port=,regexp=, flags=) 检查URL是否符合预期
  • 作者:念兮为美
  • 原文链接:https://blog.csdn.net/lvoelife/article/details/126283828
    更新时间:2023年2月19日07:55:45 ,共 4734 字。