【问题标题】:Spring boot @Valid on requestBody in controller method not working控制器方法中 requestBody 上的 Spring Boot @Valid 不起作用
【发布时间】:2020-09-11 12:17:27
【问题描述】:

我正在尝试在由@Validated 注释的@RestController 中验证由@Valid 注释注释的简单请求正文。验证在请求中的原始变量上正常工作(在下面的例子中年龄),但在 pojo 请求正文上没有工作。 @Valid 注解对请求主体 Person 类没有影响(即控制器接受空白名称和 18 岁以下的年龄)。

人物类:

import javax.validation.constraints.Min
import javax.validation.constraints.NotBlank

class Person (

    @NotBlank
    val name : String,

    @Min(18)
    val age : Int
)

控制器类:

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.RestController
import javax.validation.Valid

@RestController
@Validated
class HomeController {

    @GetMapping("/{age}")
    fun verifyAge(@PathVariable("age") @Min(18) age:Int): String {
        return "Eligible age."
    }

    @PostMapping
    fun personValidation(@Valid @RequestBody person : Person) : String {
        return "No validation error"
    }
}

@NotBlank、@Min 和 @Valid 注释来自以下依赖:

    implementation("org.springframework.boot:spring-boot-starter-validation:2.3.0.RELEASE")

如何让@Valid 在 @Validated 控制器中处理 Person 请求正文?

【问题讨论】:

  • 您应该将 BindingResult 声明为方法参数。之后,您将像这样进行验证: if(bindingResult.hasErrors()) {...}
  • 我只想使用注释和标准验证器提供程序(如 springframework 或 hibernate)进行验证,而无需任何编程方式,例如:spring.io/guides/gs/validating-form-input。我还可以使用配置类或依赖项配置我的项目,但希望保持验证干净。此外,我的期望是仅验证 pojo 类中的原始/原始值。
  • 此示例使用 BindingResult。在他们的 github 存储库中查看:github.com/spring-guides/gs-validating-form-input/blob/…

标签: spring spring-boot validation kotlin


【解决方案1】:

不要将 Person 标记为 Spring @Component。

实现字段验证的正确方法是将@field 添加到您的属性中。 示例代码:

class SomeRequest(
    @field:NotBlank val name: String
)

发生这种情况是因为 Spring Boot 需要将此注释应用于字段。 Kotlin 默认将它们应用于构造函数参数。请参阅 Kotlin 文档中的 Annotation use-site targets

【讨论】:

  • 谢谢您,先生!我搜索了大量的答案,只有你的答案真的很有帮助!
  • 很高兴我的回答对您有所帮助! :)
【解决方案2】:

在我的例子中,为了让 @Valid @RequestBody 在 springboot 应用程序中工作,必须同时使用这两个依赖项

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>2.0.1.Final</version>
    </dependency>

【讨论】:

    【解决方案3】:

    [已编辑] 正如其中一位用户回答的那样,这也对我有用(在构造函数参数上添加字段注释):

    import javax.validation.constraints.Min
    import javax.validation.constraints.NotBlank
    
    class Person (
    
        @field:NotBlank
        val name : String,
    
        @field:Min(18)
        val age : Int
    )
    

    尽管将 Person 的“kotlin”类定义更改如下(将构造函数参数更改为无参数)(注意括号 (...) 到花括号{...})也可以在不添加 @field 注释的情况下工作:

    import javax.validation.constraints.Min
    import javax.validation.constraints.NotBlank
    
    class Person {
    
        @NotBlank
        lateinit var name: String
    
        @Min(18)
        var age: Int = 0
    }
    

    【讨论】:

      【解决方案4】:

      你可以试试这个选项。

      请使用@PostMapping("/validateBody") 之类的URL 映射@PostMapping,并尝试使用它的URL。正如https://reflectoring.io/bean-validation-with-spring-boot/所做的那样

      【讨论】:

      • 这只是验证示例中的路径。根据项目,该路径可以是“/users”、“/products”之类的任何路径。我共享的帖子映射工作正常,即我成功获得了返回的响应“无验证错误”,只是 @Valid 注释没有任何效果。
      【解决方案5】:

      以防万一有人使用@BasePathAwareController@RepositoryRestController 注释。 @Valid 目前在 @RepositoryRestController@BasePathAwareController 上不受支持。

      Spring Data REST 中有一个未解决的问题:https://github.com/spring-projects/spring-data-rest/issues/967

      【讨论】:

        【解决方案6】:

        Piotr Solarski 是正确的(上图),将组件添加到请求主体是不正确的,因为主体会发生变化,并且稍后为它创建单个对象可能会变得很棘手(即使它解决了 OP 的问题)。

        在 MVC 应用程序中,这就是我的问题所在。

        我添加了你通常做的东西(pom 中的库、RequestBody 上的@Valid 等)

        Spring 文档(以及许多博客)留下的微妙之处在于 Spring 寻找一个退出点来抛出错误,但如果该退出不存在,它将回复 404。在阅读了很多之后,尤其是这个 @ 987654321@,添加这个类让Spring识别@Valid并找到退出点抛出错误

            @RestControllerAdvice
            @Order(1) 
            public class RestControllerExceptionHandler {
        
            @RequestMapping(value = { "error/404" }, method = RequestMethod.GET)
            @ExceptionHandler(Exception.class)
            public String handleUnexpectedException(Exception e) {
                return "views/base/rest-error";         
            }
        
            @ResponseStatus(HttpStatus.BAD_REQUEST)
            @ExceptionHandler(MethodArgumentNotValidException.class)
            public String handlemethodArgumentNotValid(MethodArgumentNotValidException exception) { //
                // TODO you can choose to return your custom object here, which will then get transformed to json/xml etc.
                return "Sorry, that was not quite right: " + exception.getMessage();
            }
        
            @ResponseStatus(HttpStatus.BAD_REQUEST)
            @ExceptionHandler(ConstraintViolationException.class)
            public String handleConstraintViolation(ConstraintViolationException exception) { //
                // TODO you can choose to return your custom object here, which will then get transformed to json/xml etc.
                return "Sorry, that was not quite right: " + exception.getMessage();
            }
        
            
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-06-16
          • 1970-01-01
          • 2017-10-18
          • 1970-01-01
          • 2022-01-01
          • 2020-07-30
          • 2016-01-24
          相关资源
          最近更新 更多