【SpringMVC应用篇】Spring Validation 参数校验

1. Spring Validation

JSR-303 简介

JSR-303 是 JavaEE 6 中的一项子规范,叫做 Bean Validation,官方参考实现是 Hibernate Validator

此实现与 Hibernate ORM 没有任何关系。JSR-303 用于对 Java Bean 中的字段的值进行验证。 Spring MVC 3.x 之中也大力支持 JSR-303,可以在控制器中使用注解的方式对表单提交的数据方便地验证。Spring 4.0 开始支持 Bean Validation 功能。在日常开发中,Hibernate Validator经常用来验证bean的字段,基于注解,方便快捷高效。

2. JSR-303 基本的校验规则

2.1空检查

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

使用hibernate validator出现上面的错误, 需要注意@NotNull 和 @NotEmpty 和@NotBlank 区别

@NotEmpty 用在集合类上面
@NotBlank 用在String上面

@NotNull 用在基本类型上

在枚举类上不要加非空注解

2.2 布尔检查

  • @AssertTrue 验证Boolean对象是否为 true
  • @AssertFalse 验证 Boolean 对象是否为 false

2.3 长度检查

  • @Size(min=, max=) 验证对象(Array, Collection , Map, String)长度是否在给定的范围之内
  • @Length(min=, max=) 验证字符串长度介于 min 和 max 之间

2.4 日期检查

  • @Past 验证 DateCalendar 对象是否在当前时间之前,验证成立的话被注释的元素一定是一个过去的日期
  • @Future 验证Date Calendar 对象是否在当前时间之后 ,验证成立的话被注释的元素一定是一个将来的日期

2.5 正则检查

  • @Pattern 验证 String 对象是否符合正则表达式的规则,被注释的元素符合制定的正则表达式
    • regexp:正则表达式
    • flags:指定Pattern.Flag的数组,表示正则表达式的相关选项

2.6 数值检查

注意: 建议使用在 String ,Integer 类型,不建议使用在int类型上,因为表单值为 “” 时无法转换为 int,但可以转换为 String“”Integer null

  • @Min 验证 Number 和 String 对象是否大等于指定的值
  • @Max 验证 Number 和 String 对象是否小等于指定的值
  • @DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过 BigDecimal 定义的最大值的字符串表示 .小数 存在精度
  • @DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过 BigDecimal 定义的最小值的字符串表示 .小数 存在精度
  • @Digits 验证 NumberString 的构成是否合法
  • @Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字,integer 指定整数精度,fraction 指定小数精度
  • @Range(min=, max=) 被指定的元素必须在合适的范围内
  • @Range(min=10000,max=50000,message=”range.bean.wage”)
  • @CreditCardNumber 信用卡验证
  • @Email 验证是否是邮件地址,如果为 null,不进行验证,算通过验证
  • @ScriptAssert(lang= ,script=, alias=)
  • @URL(protocol=,host=, port=,regexp=, flags=)
@Range(min = 1, max = 4, message = "统筹类型不合法", groups = {ValidGroup.Insert.class, ValidGroup.Update.class})
@NotNull(message = "统筹类型不能为空!", groups = {ValidGroup.Insert.class, ValidGroup.Update.class})
private Integer overallType;


@Digits(integer = 1, fraction = 2, message = "统筹比例格式不正确!", groups = {ValidGroup.Insert.class, ValidGroup.Update.class})
@DecimalMin(message = "请填写0-1的比例!", value = "0.00", groups = {ValidGroup.Insert.class, ValidGroup.Update.class})
@DecimalMax(message = "请填写0-1的比例!",value = "1.00", groups = {ValidGroup.Insert.class, ValidGroup.Update.class})
@NotNull(message = "统筹比例不能为空!", groups = {ValidGroup.Insert.class, ValidGroup.Update.class})
private BigDecimal overallProportion;

1、@Digits:digit是数位的意思,这里的integer意思整数最多有几位,fraction意思小数最多有几位,只是划定了传入参数的范围,前端传值的时候可以用字符串(只要字符串里面都是数字就行)、数字;
2、@DecimalMin:decimal的最小值,传入参数必须大于等于value里面的值;
3、@NotNull:传入参数不为空,对于BigDecimal格式,用@NotNull注解,如果传参是空字符串或者只有空格的字符串,也无法通过校验。如果字段的类型是String,建议使用@NotBlank注解。

2.7 对象校验

  • @Valid 被注释的元素是一个对象,需要检查此对象的所有字段值
  • @Validated 被注解的元素是一个对象或者一个类,需要检查此对象的所有字段值

3 使用 Spring Validation 验证

POM

如果spring-boot版本小于2.3.x,spring-boot-starter-web会自动传入hibernate-validator依赖。
如果spring-boot版本大于2.3.x,则需要手动引入依赖:
在这里插入图片描述

这里我们使用spring-boot:2.2.2.RELEASE ,Hibernate Validator :5.x 来实现Spring Validation接口,pom.xml 文件如下:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.3.4.Final</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

主要是增加了 org.hibernate:hibernate-validator 依赖

实体类参数校验

User

import lombok.Data;
import org.hibernate.validator.constraints.Email;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

//使用Spring Validator和Hibernate Validator这两套Validator来进行方便的参数校验!
//这两套Validator依赖包已经包含在前面所说的web依赖包里了,所以可以直接使用。所以可以直接使用
@Data
public class User {

    @NotNull(message = "用户名id不能为空")
    private Long id;

    @NotNull(message = "用户账号不能为空")
    @Size(min = 6, max = 11, message = "账号长度必须是6-11个字符")
    private String account;

    @NotNull(message = "用户密码不能为空")
    @Size(min = 6, max = 11, message = "密码长度必须是6-16个字符")
    private String password;


    //@Email使用的是org.hibernate包下的注解
    // javax下的包会报错,坑
    @NotNull(message = "用户邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;
    
}

在这里插入图片描述

controller

实体类上加上@Valid注解,使用json传参,加@RequestBody解析json参数映射为实体类,如果校验错误不会执行业务逻辑,抛出异常

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/addUser")
    //@Valid 被注释的元素是一个对象,需要检查此对象的所有字段值
	//@Validated 被注解的元素是一个对象或者一个类,需要检查此对象的所有字段值
    //public String addUser(@RequestBody @Valid User user,BindingResult bindingResult) {
    public String addUser(@RequestBody @Valid User user) {  //BindingResult bindingResult
        
        // 如果出现异常,后面的代码不会执行
        // 如果有参数校验失败,会将错误信息封装成对象组装在BindingResult里
//        for (ObjectError error : bindingResult.getAllErrors()) {
//            return error.getDefaultMessage();
//        }
        return userService.save(user);
    }
}

service

public interface UserService {
    String  save(User user);
}


@Service
public class UserServiceImpl implements UserService {
    @Override
    public String save(User user) {
        System.out.println("保存成功---->"+user);
        return "success";
    }
}

PostMan测试

发送post请求

{
    "id": 1,
    "account": "12345678",
    "password": "12312123",
    "email": "15096021" # 错误格式
}

响应数据

数据不友好,使用全局异常捕获返回友好提示。

{
    "timestamp": "2020-09-19T06:00:38.546+00:00",
    "status": 400,
    "error": "Bad Request",
    "trace": "org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public java.util.List cn.zysheep.springboot_interface.controller.UserController.addUser(cn.zysheep.springboot_interface.entity.User): [Field error in object 'user' on field 'email': rejected value [15096021m]; codes [Email.user.email,Email.email,Email.java.lang.String,Email]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.email,email]; arguments []; default message [email],[Ljavax.validation.constraints.Pattern$Flag;@3796b0b3,.*]; default message [邮箱格式不正确]] \r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:139)\r\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:167)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:878)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:792)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:652)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:733)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\r\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\r\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.lang.Thread.run(Thread.java:748)\r\n",
    "message": "Validation failed for object='user'. Error count: 1",
    "errors": [
        {
            "codes": [
                "Email.user.email",
                "Email.email",
                "Email.java.lang.String",
                "Email"
            ],
            "arguments": [
                {
                    "codes": [
                        "user.email",
                        "email"
                    ],
                    "arguments": null,
                    "defaultMessage": "email",
                    "code": "email"
                },
                [],
                {
                    "arguments": null,
                    "defaultMessage": ".*",
                    "codes": [
                        ".*"
                    ]
                }
            ],
            "defaultMessage": "邮箱格式不正确",
            "objectName": "user",
            "field": "email",
            "rejectedValue": "15096021m",
            "bindingFailure": false,
            "code": "Email"
        }
    ],
    "path": "/addUser"
}

全局处理异常

全局处理异常,处理 @RequestBody参数校验异常,统一返回格式自定义

@RestControllerAdvice
public class ExceptionControllerAdvice {
    /**
     * 处理 @RequestBody参数校验异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Map<String, Object> MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
     	
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

        // 获取所有异常
        List<String> errors = e.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(DefaultMessageSourceResolvable::getDefaultMessage)
                .collect(Collectors.toList());
        body.put("errors", errors);
        return body;
    }
}

PostMan测试,响应数据

# 请求数据
{
    "id": 1,
    "account": "12345678",
    "password": "12312123",
    "email": "15096021m"
}

# 全局异常处理后的响应数据
{
    "timestamp": "2020-09-19 14:07:05",
    "errors": [
        "邮箱格式不正确"
    ]
}

键值对Map错误信息

@RestControllerAdvice
public class ExceptionControllerAdvice {
    /**
     * 处理 @RequestBody参数校验异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Map<String, Object> MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
     	
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

        // 获取所有异常
        HashMap<String, String> map = new HashMap<>();
       ((MethodArgumentNotValidException) e).getBindingResult()
                .getFieldErrors()
                //.map(DefaultMessageSourceResolvable::getDefaultMessage)
                .forEach(x->{
                    map.put(x.getField(),x.getDefaultMessage());
                });
                //.collect(Collectors.toMap(FieldError::getField, DefaultMessageSourceResolvable::getDefaultMessage));
        body.put("errors", map);
        return body;
    }
}

在这里插入图片描述

表单传参校验

使用表单传参,即不使用@RequestBody,跟上面的第一类异常捕获的异常类型不同而已。

@Controller
public class LoginController {

    @PostMapping("/test1")
    @ResponseBody
    public AjaxResult test1(@Validated User user){
        System.out.println(user);
        return user;
    }
    
} 
@RestControllerAdvice
public class GlobalExceptionHandler {

  @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultVO<Map<String, Object>> MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        // 从异常对象中拿到ObjectError对象
        //ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        // 然后提取错误提示信息进行返回

        Map<String, Object> body = new LinkedHashMap<>();
        body.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

        // 获取所有异常
        List<String> errors = e.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(DefaultMessageSourceResolvable::getDefaultMessage)
                .collect(Collectors.toList());
        body.put("errors", errors);

        return new ResultVO<>(ResultCode.FAILED,body);
    }
}

单个参数校验

直接在参数前加上校验注解

@RestController
@Validated
public class UserController  {
     @GetMapping("/test3")
     public AjaxResult test3(@NotNull(message = "name不能为空")  String name ,@Email(message ="邮箱格式不正确") String email){
        System.out.println(name);
        System.out.println(email);
        return AjaxResult.success(name+" "+email);
    }
}

注意:需要在类上添加@Validated注解,否则不会校验。

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    /**
     * 处理所有参数校验时抛出的异常
     * @param ex
     * @return
     */
    @ExceptionHandler(value = ValidationException.class)
    public AjaxResult handleBindException(ValidationException ex) {
        AjaxResult body = new AjaxResult();
    
        body.put("timestamp", new Date());
    
        // 获取所有异常
        List<String> errors = new LinkedList<>();
        if (ex instanceof ConstraintViolationException) {
            ConstraintViolationException exs = (ConstraintViolationException) ex;
            Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
            for (ConstraintViolation<?> item : violations) {
                errors.add(item.getMessage());
            }
        }
        body.put("errors", errors);
        body.put("code",900);
        body.put("msg","提交的数据校验失败");
    
        return body;
    }

}

@Validated和@Valid的区别

一般情况下,若不需要分组校验的话:使用@Valid和@Validated并无特殊差异。

1、@Valid:@Valid注解时javax包下的注解,是jdk给提供的。标准JSR-303规范的标记型注解
2、@Validated:@Validated是@Valid 的一次封装,是Spring提供的校验机制使用。Spring的注解。是标准JSR-303的一个变种(补充),提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制。
3、@Validated只能用在类、方法和参数上,而@Valid可用于方法、字段、构造器和参数上。两者是否能用于成员属性(字段)上直接影响能否提供嵌套验证的功能,显然@Valid注解可以提供嵌套校验的功能,@Validated除了没有嵌套校验的功能,其他@Valid注解有的功能,它都有,@Valid注解没有的功能,它也有

分组校验

在实际开发中经常会遇到这种情况:添加用户时,id是由后端生成的,不需要校验id是否为空,但是修改用户时就需要校验id是否为空。如果在接收参数的User实体类的id属性上添加NotNull,显然无法实现。这时候就可以定义分组,在需要校验id的时候校验,不需要的时候不校验。

1、定义分组


/**
 *分组校验 (配合spring的@Validated功能分组使用)
 */
public class ValidGroup {
    // 新增使用
    public interface Insert{}

    // 更新使用
    public interface Update{}

    // 删除使用
    public interface Delete{}

    // 属性必须有这两个分组的才验证
    @GroupSequence({Insert.class, Update.class,Delete.class})
    public interface All{}
}

2、 实体类中属性validation注解使用上面定义的组
在每个注解中有一个属性groups

//只能在Delete和Update的时候才能够进行生效.
@Min(value = 1,message = "ID不能小于1",groups = {ValidGroup.Delete.class,ValidGroup.Update.class})
private int id;

@NotBlank(message = "用户名不能为空",groups = {ValidGroup.Update.class,ValidGroup.Insert.class})
private String username;

@NotBlank(message = "密码不能为空",groups = {ValidGroup.Update.class,ValidGroup.Insert.class})
@Length(min = 6,max = 20,message = "密码长度在6-20之间")
private String password;

@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不合理")
private String email;

3、controller中使用@Validated指定使用哪个组

@RequestMapping("/saveUserInfo")
public UserInfo saveUserInfo(@Validated() UserInfo userInfo){
    //save userInfo:将userInfo进行保存
    //userInfoService.save(userInfo);
    return userInfo;
}

@RequestMapping("/updateUserInfo")
public UserInfo updateUserInfo(@Validated({ValidGroup.Update.class}) UserInfo userInfo){
    //save userInfo:将userInfo进行保存
    //userInfoService.update(userInfo);
    return userInfo;
}


@RequestMapping("/deleteUserInfo")
public UserInfo deleteUserInfo(@Validated({ValidGroup.Delete.class}) UserInfo userInfo){
    //save userInfo:将userInfo进行保存
    //userInfoService.delete(userInfo);
    return userInfo;
}

嵌套校验

public class Item {

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

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

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;
}

现在我们有个ItemController接受一个Item的入参,想要对Item进行验证,如下所示:

@RestController
public class ItemController {

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

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

为了能够进行嵌套验证,必须手动在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<Prop> props;
}

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

总结一下@Validated和@Valid在嵌套验证功能上的区别:

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

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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李熠漾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值