Spring中的@Valid注解表示什么?

124
在下面的示例中,ScriptFile参数带有一个@Valid注释。 @Valid注释的作用是什么?
@RequestMapping(value = "/scriptfile", method = RequestMethod.POST)    
public String create(@Valid ScriptFile scriptFile, BindingResult result, ModelMap modelMap) {    
    if (scriptFile == null) throw new IllegalArgumentException("A scriptFile is required");        
    if (result.hasErrors()) {        
        modelMap.addAttribute("scriptFile", scriptFile);            
        modelMap.addAttribute("showcases", ShowCase.findAllShowCases());            
        return "scriptfile/create";            
    }        
    scriptFile.persist();        
    return "redirect:/scriptfile/" + scriptFile.getId();        
}    
9个回答

82

这是为了验证目的。

验证 绑定用户输入到模型后进行验证非常常见。Spring 3 提供了支持 JSR-303 声明式验证的功能。如果类路径上存在 JSR-303 提供程序(如 Hibernate Validator),则此支持将自动启用。启用后,您可以通过在控制器方法参数上注释 @Valid 来触发验证: 在绑定传入的 POST 参数之后,将验证 AppointmentForm; 在本例中,以验证日期字段值不为 null,并且在未来发生。


有关更多信息,请查看:
http://blog.springsource.com/2009/11/17/spring-3-type-conversion-and-validation/


14
简而言之,在你提供的代码示例中,ScriptFile 类对类数据成员(如 @NotNull@NotEmpty 等)进行了约束,而 @Valid 指示框架(在我们这里是 Spring)检查当某人调用您的方法时提供的参数是否符合这些约束。在这种情况下,如果验证失败,服务器将响应带有 HTTP 400 Bad Request 状态码的请求。 - Emre Tapcı
@EmreTapcı 如果我们不输入有效值会发生什么?它不会验证任何约束吗? - LowCool
1
如果使用@Valid标记的实体为null会发生什么? - D.Kaushish

45

除了以上的回答,看一下以下的内容。AppointmentFormdate 列被注释了几个注释。通过具有触发对 AppointmentForm 进行验证的 @Valid 注释(在这种情况下为 @NotNull@Future),这些注释可以来自不同的 JSR-303 提供者(例如 Hibernate、Spring 等)。

    @RequestMapping(value = "/appointments", method = RequestMethod.POST)
    public String add(@Valid AppointmentForm form, BindingResult result) {
        ....
    }

    static class AppointmentForm {

        @NotNull @Future
        private Date date;
    }

4
我有类似的代码。我已经移除了ApplicationForm参数上的@Valid,但仍然对date字段(设为null)进行验证。请解释一下。 - lupchiazoem

26

我想添加更多关于 @Valid 如何工作的细节,特别是在 Spring 中的使用。

有关在 Spring 中进行验证的所有信息都可以在https://reflectoring.io/bean-validation-with-spring-boot/中清晰详细地解释,但如果链接失效,我将复制答案说明如何使用 @Valid

在 rest 控制器方法中,可以向变量添加 @Valid 注释以对其进行验证。可以验证三种类型的变量:

  • 请求体
  • 路径中的变量(例如 /foos/{id} 中的 id)
  • 查询参数

那么现在...Spring 如何“验证”?您可以通过注释来定义类的字段约束条件。然后,您将该类的对象传递给 Validator,它会检查是否满足约束条件。

例如,假设我有以下控制器方法:

@RestController
class ValidateRequestBodyController {

  @PostMapping("/validateBody")
  ResponseEntity<String> validateBody(@Valid @RequestBody Input input) {
    return ResponseEntity.ok("valid");
  }

}

这是一个使用POST请求的操作,它需要一个请求体,然后我们将该请求体映射到一个名为Input的类。

这是Input类:

class Input {

  @Min(1)
  @Max(10)
  private int numberBetweenOneAndTen;

  @Pattern(regexp = "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$")
  private String ipAddress;
  
  // ...
}
@Valid注释将告诉Spring去验证传递到控制器中的数据,检查整数 numberBetweenOneAndTen 是否在1到10之间(包括1和10),因为有最小值和最大值注释。它还会检查传递的IP地址是否与注释中的正则表达式匹配。

附注:正则表达式不完美...您可以传递大于255的3位数字,它仍将匹配正则表达式。


这是一个验证查询变量和路径变量的示例:

@RestController
@Validated
class ValidateParametersController {

  @GetMapping("/validatePathVariable/{id}")
  ResponseEntity<String> validatePathVariable(
      @PathVariable("id") @Min(5) int id) {
    return ResponseEntity.ok("valid");
  }
  
  @GetMapping("/validateRequestParameter")
  ResponseEntity<String> validateRequestParameter(
      @RequestParam("param") @Min(5) int param) { 
    return ResponseEntity.ok("valid");
  }
}

在这种情况下,由于查询变量和路径变量仅为整数,而不是复杂类,因此我们将约束注释@Min(5)直接放在参数上,而不是使用@Valid


1
所以这是一个接收响应体的POST请求,我们将该响应体映射到一个名为Input的类中......难道不应该是接收"请求体"的POST请求吗? - sss
@sss:是的,那是个错误。现在已经被纠正了。 - SomeGuy
1
@SomeGuy 我只是想知道如果:
  1. 我将值发送为“null” - @ Valid @ RequestBody Input input,会有什么结果
  2. 我将数字介于1和10之间的值传递为0,会发生什么。
- EnthuCoder
@EnthuCoder:其实我也不确定…… 我认为null被认为是有效的,如果你不想将null视为有效值,则必须使用@NonNull。 - SomeGuy

26

@Valid 本身与 Spring 没有关系,它是 Bean Validation 规范的一部分(有几个版本,截至2017年下半年最新的是 JSR 380),但 @Valid 非常古老,最初源自于 JSR 303。

众所周知,Spring 在提供与各种 JSR 和 Java 库集成方面非常擅长(例如 JPA、JTA、Caching 等等),当然也包括验证。其中一个关键组件是 MethodValidationPostProcessor

回答您的问题——@Valid 对于所谓的验证级联非常方便,当您要验证复杂的图形而不仅仅是对象的顶级元素时,每次想要深入更多层次时,都必须使用 @Valid。这就是JSR规定的。Spring会遵守这些规定,但会有一些细微的偏差(例如我在 RestController 方法中尝试将 @Valid 替换为 @Validated,仍能正常验证,但对于普通的“service” bean则不适用)。


6
但是,“validate”是什么意思? - nephewtom
请详细说明,@nephewtom。您想要澄清什么? - yuranos
我阅读了JSR 303: Bean Validation,https://beanvalidation.org/1.0/spec/,但我仍然不清楚将在`ScriptFile scriptFile`上执行哪些验证。 - nephewtom
ScriptFile里面可能有一堆字段,这些字段也有注释。这就是验证开始的地方。根据原始问题,ScriptFile类里面到底有什么并不清楚。 - yuranos
好的,谢谢。 你能举个例子来验证它的字段是否为字符串、整数和Bean吗? - nephewtom
像这样使用@Email(message = "Email should be valid")修饰的私有字符串email,如果你使用最新的支持电子邮件的规范,则可以使其有效。或者,你也可以使用@Pattern(message="Invalid pattern.", regexp = "^.+@.+\.")修饰私有字符串pattern。 - yuranos

20

IIRC @Valid 不是一个 Spring 的注解,而是一个 JSR-303 的注解(即 Bean Validation 标准)。它的作用是基本上检查您发送到方法的数据是否有效(它将为您验证 scriptFile)。


4
“it will validate the scriptFile for you” 的意思是它会为您验证脚本文件。这包括检查它是否为空、是否有正确的语法以及是否有一些内容。换句话说,它会验证什么以及如何进行验证?用户需要实现什么?关于此信息在哪里可以获得更多资料?谢谢! - nephewtom
2
请回答@nephewtom的问题,仅仅回答缺失的主要观点是“反对什么”是不够的。 - monami

4
public String create(@Valid @NotNull ScriptFile scriptFile, BindingResult result, ModelMap modelMap) {    
    if (scriptFile == null) throw new IllegalArgumentException("A scriptFile is required");        

我猜这个@NotNull注释是有效的,因此不需要if条件。

4

我想我知道你要问什么。既然这个问题是谷歌搜索主结果中出现的,我可以简单地解释一下@Valid注释的作用。

我将呈现3种使用@Valid的场景:

模型:

public class Employee{
private String name;
@NotNull(message="cannot be null")
@Size(min=1, message="cannot be blank")
private String lastName;
 //Getters and Setters for both fields.
 //...
}

JSP:
...
<form:form action="processForm" modelAttribute="employee">
 <form:input type="text" path="name"/>
 <br>
 <form:input type="text" path="lastName"/>
<form:errors path="lastName"/>
<input type="submit" value="Submit"/>
</form:form>
...

场景 1 的控制器:

     @RequestMapping("processForm")
        public String processFormData(@Valid @ModelAttribute("employee") Employee employee){
        return "employee-confirmation-page";
    }

在这种情况下,如果您提交的表单中lastName字段为空,则会因为应用了验证规则但未进行处理而得到错误页面。
所示错误的示例: 异常页面 第二个方案的控制器:
 @RequestMapping("processForm")
    public String processFormData(@Valid @ModelAttribute("employee") Employee employee,
BindingResult bindingResult){
                return bindingResult.hasErrors() ? "employee-form" : "employee-confirmation-page";
            }

在这种情况下,您将所有验证结果传递给bindingResult,因此需要由您决定如何处理该表单的验证结果。
方案3的控制器:
@RequestMapping("processForm")
    public String processFormData(@Valid @ModelAttribute("employee") Employee employee){
                return "employee-confirmation-page";
            }
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> invalidFormProcessor(MethodArgumentNotValidException ex){
  //Your mapping of the errors...etc
}

在这种情况下,你仍然没有像第一种情况那样处理错误,但你将其传递给另一个方法来处理@Valid在处理表单模型时触发的异常。查看这个链接了解如何处理映射以及其他问题。 总之:单独使用@Valid只会触发验证验证JSR 303注释字段(@NotNull、@Email、@Size等),你仍然需要指定处理验证结果的策略。
希望我能够对可能遇到此问题的人们解释清楚一些内容。

2

补充以上答案,@valid在web应用程序中使用时,需要验证的bean也要用验证注释进行注释,例如@NotNull@Email(hibernate注释),因此在从用户获取输入时,可以验证值并且绑定结果将有验证结果。bindingResult.hasErrors()将告知是否存在任何验证失败。


2

@Valid 的另一个方便之处是,当使用 Postman 测试终端点时,它将把不正确的 REST 调用的输出格式化为 JSON 格式,而不是一堆难以阅读的文本。如果您正在为用户创建商业可消费的 API,这非常有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接