控制器方法中 requestBody 上的 Spring Boot @Valid 不起作用

Posted

技术标签:

【中文标题】控制器方法中 requestBody 上的 Spring Boot @Valid 不起作用【英文标题】:Spring boot @Valid on requestBody in controller method not working 【发布时间】: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/… 【参考方案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();
    

    

【讨论】:

以上是关于控制器方法中 requestBody 上的 Spring Boot @Valid 不起作用的主要内容,如果未能解决你的问题,请参考以下文章

限制 @RequestBody 的 JSON 属性

WEB之ResponseBody RequestBody RequestParam

为啥@RequestBody 得到一个具有空属性的对象

requestbody注解

@RequestBody 没有将 JSON 映射到 Java 对象 - Spring Boot

在 Swagger 中记录 @RequestBody 映射