为什么在验证Spring控制器请求参数时需要使用@Validated?

huangapple go评论89阅读模式
英文:

Why is @Validated required for validating Spring controller request parameters?

问题

使用如下的验证设置在一个带注解的MVC控制器中:

@RestController
@RequestMapping("/users")
@Validated  // <-- 如果没有这个注解,setPassword() 方法中的 @Size 注解将无效
public class UserController {

    @PutMapping("/{id}/password")
    public void setPassword(@PathVariable long id, @RequestBody @Size(min = 8) String password) {
        /* ... */
    }

    @PutMapping("/{id}/other")
    public void setOther(@PathVariable long id, @RequestBody @Valid MyFormObject form) {
        /* ... */
    }
}

在控制器上使用 @Validated 注解是因为方法参数不是一个“复杂”对象。相比之下,setOther 方法上的 @Valid 注解可以在没有 @Validated 注解的情况下工作。

为什么需要 @Validated 注解?为什么不默认启用它?使用它会有什么成本?

编辑

请注意,https://stackoverflow.com/questions/36173332/difference-between-valid-and-validated-in-spring 是相关的(我在提问之前已经阅读了该链接),但它没有解答我问题中的“为什么”。

英文:

With the following validation setup in an annotated MVC controller:

@RestController
@RequestMapping(&quot;/users&quot;)
@Validated  // &lt;-- without this, the @Size annotation in setPassword() has no effect
public class UserController {

    @PutMapping(&quot;/{id}/password&quot;)
    public void setPassword(@PathVariable long id, @RequestBody @Size(min = 8) String password) {
        /* ... */
    }

    @PutMapping(&quot;/{id}/other&quot;)
    public void setOther(@PathVariable long id, @RequestBody @Valid MyFormObject form) {
        /* ... */
    }
}

@Validated on the controller is required for the method parameter since it's not a "complex" object. In comparison, the @Valid annotation on the setOther method works without the @Validated annotation.

Why is @Validated required? Why not enable it by default? Is there a cost to its use?

edit

Note that https://stackoverflow.com/questions/36173332/difference-between-valid-and-validated-in-spring is related (I read it before asking this), but it doesn't address the why in my question.

答案1

得分: 16

对象的验证是由Hibernate Validator执行的,使用来自Jakarta Bean Validation 2.0的注释。需要有某种触发机制来运行Hibernate验证器。

当SpringMVC看到带有@Valid参数时,它会调用控制器方法,并将该对象传递给Hibernate验证器。Hibernate验证器将会:

  1. 检查对象的类,以确定已放置在类字段上的验证规则
  2. 针对带有验证注释的字段执行验证规则。

因此在这种情况下:

@PutMapping("/{id}/other")
public void setOther(@PathVariable long id, @RequestBody @Valid MyFormObject form) {
        /* ... */
}

MyFormObject 上有注释,Hibernate验证器可以找到这些注释来验证对象。

在这种情况下:

@PutMapping("/{id}/password")
public void setPassword(@PathVariable long id, @RequestBody @Size(min = 8) String password) {
        /* ... */
}

java.lang.String上没有定义任何注释供Hibernate验证器发现。

@Valid来自Bean验证包javax.validation.Valid,而@Validated来自Spring的org.springframework.validation.annotation.Validated

@Validated注解会激活Spring验证AOP拦截器,它会检查方法参数,以查看它们是否具有任何验证注释,如果有,Spring将会调用Hibernate验证器,并使用每个特定的注释。例如@Size(min = 8) String password意味着调用Hibernate大小验证器,并传递参数password的值,在这种情况下,Hibernate验证器无需扫描java.lang.String来查看它是否具有验证注释。@Validated适用于任何Spring @Component,您可以在@Service类上使用它,例如。

使用@Validated会增加额外的开销,类似于使用@Transactional,因此您必须选择使用它。在使用javax.validation.Valid的情况下,Spring MVC需要检查控制器方法参数上的注释,因此当它看到@Valid时,它很容易将该对象发送给Hibernate验证器,而无需添加AOP拦截器。

英文:

Validation of objects is done by Hibernate Validator using the annotations from Jakarta Bean Validation 2.0. Something needs to trigger hibernate validator to run.

SpringMVC calls the controller methods when it sees a parameter with @Valid it will pass that object to hibernate validator. Hibernate validator will

  1. Examine the class of the object to figure out what validation rules have been put on the class fields
  2. Execute the validation rules against the fields marked up with validation annotations.

So in this case

@PutMapping(&quot;/{id}/other&quot;)
public void setOther(@PathVariable long id, @RequestBody @Valid MyFormObject form) {
        /* ... */
} 

MyFormObject has annotations on it that the hibernate validator can find to validate the object.

In this case

@PutMapping(&quot;/{id}/password&quot;)
public void setPassword(@PathVariable long id, @RequestBody @Size(min = 8) String password) {
        /* ... */
}

java.lang.String does not have any annotations defined on it for hibernate validator to discover.

@Valid comes from the Bean validation package javax.validation.Valid while @Validated comes from Spring org.springframework.validation.annotation.Validated

@Validated annotation activates the Spring Validation AOP interceptor and it will examine method parameters to see if they have any validation annotations on them, if they do then Spring will call hibernate validator with each specific annotation for example @Size(min = 8) String password means call hibernate size validator and pass the value of the parameter password in this case hibernate validator does not need to scan java.lang.String to see if it has validation annotations on it. @Validated works on any spring @Component you can use it on @Service classes for example.

There is extra overhead for using @Validated similar to using @Transactional so that is why you have to opt into it. In the case of javax.validation.Valid Spring MVC needs to check the annotations on the controller method parameters so when it sees @Valid it is easy for it to send that object the Hibernate Validator without needing to add an AOP interceptor.

huangapple
  • 本文由 发表于 2020年7月26日 23:56:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/63102468.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定