Spring Boot - 验证@RequestBody

Posted

技术标签:

【中文标题】Spring Boot - 验证@RequestBody【英文标题】:Springboot - validate @RequestBody 【发布时间】:2021-02-07 13:17:27 【问题描述】:

问题:是否可以验证请求正文的 JSON 有效负载,而无需专门编写 if 语句?也许通过注释或配置?

我有一个非常简单的 POJO:

public class Foo 

    private int important;
    private String something;

//constructors, getter, seters, toString

还有一个非常简单的控制器类:

@SpringBootApplication
@RestController
public class QuestionController 

    public static void main(String[] args) 
        SpringApplication.run(QuestionController.class, args);
    

    @GetMapping(value = "/question")
    Mono<String> question(@RequestBody Foo foo) 
        System.out.println("The object foo, with value for important = " + foo.getImportant() + " and something = " + foo.getSomething());
        return Mono.just("question");
    


如果我使用以下负载进行查询:


    "important": 42,
    "something": "value"

一切正常,非常开心。

但是,如果有错字:(注意“重要”上的错字)


    "importantWithTypo": 42,
    "something": "value"

或缺少所需的“重要”(注意 JSON 甚至不完整)


    "something": "value"

请求和计算仍然有效!而“重要”的值为0!

我不希望 Spring 默认为 0 并认为一切都很好。

我也不想将我的类型从原始类型更改为装箱对象。

不用我写类似的东西:

 @GetMapping(value = "/question")
    Mono<String> question(@RequestBody Foo foo) 
        if (0 == foo.getImportant()) 
            throw new IllegalArgumentException();
        
        System.out.println("The object foo, with value for important = " + foo.getImportant() + " and something = " + foo.getSomething());
        return Mono.just("question");
    

解决此问题的最有效方法是什么?某种注释?或者也许是 Spring 启动配置?

谢谢

【问题讨论】:

是的,您使用的是原语,这就是您得到 0 的原因。您可以使用 Integer,然后当您不发送节点时您将获得 null。您可以使用 @NotNull 来验证该字段。并在@RequestBody 上使用@Valid 一篇关于验证的好文章here 请问是否有可能在保持 int 类型而不是 Integer 的情况下实现相同的效果? 是的,您可以使用@Min(1) 并在@RequestBody 上使用@Valid。阅读here了解详情 reflectoring.io/bean-validation-with-spring-boot 【参考方案1】:

在字段上添加@NotNull注解(可能需要将类型改为Integer),在控制器的方法参数上添加@Valid注解。

Mono<String> question(@Valid @RequestBody Foo foo) 
    ...

public class Foo 

    @NotNull
    private Integer important;
    private String something;

//constructors, getter, seters, toString

您可以在这里找到更多信息:https://lmonkiewicz.medium.com/the-power-of-spring-rest-api-validation-77be83edef

【讨论】:

请问是否有可能实现相同的保持类型 int,而不是 Integer? (为帖子点赞) 你可以这样配置Jackson,当它尝试使用DeserializationFeaturenull(缺少的字段)反序列化为int时会抛出DeserializationException:FAIL_ON_NULL_FOR_PRIMITIVESfasterxml.github.io/jackson-databind/javadoc/2.4/index.html?com/… @ŁukaszMonkiewicz 链接已损坏 @RBz 试试这个:lmonkiewicz.medium.com/… 您还需要将这些添加为依赖项才能使用 @Valid@NotNull 等。 &lt;dependency&gt; &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt; &lt;artifactId&gt;spring-boot-starter-validation&lt;/artifactId&gt; &lt;/dependency&gt;【参考方案2】:

已经提供的answer 涵盖了答案。

不过,我想详细说明您问的一件事。

如何在这方面失败 "importantWithTypo": 42,

它的两个方面。

    如果必填字段不存在,您希望返回 4XX(这可以通过已经给出的答案来实现) -- @NonNull/@NonEmpty 与 @Validate 注释结合使用 您想在存在未知字段importantWithTypo 时出错。这可以通过杰克逊的 fail_on_unknown_properties 属性来实现。 (可能默认为fail_on_unknown_properties = enabled,我没有检查所以不确定)。

不要做这第二件事。这将使您的 2 个服务紧密耦合。通过执行此 fail_on_unknown_properties = enabled,您将失去以不间断方式增强消费者/呼叫者服务的潜在机会。您必须协调两个应用程序的发布。

【讨论】:

以上是关于Spring Boot - 验证@RequestBody的主要内容,如果未能解决你的问题,请参考以下文章

在 Tomcat 上使用 Spring Boot 进行身份验证

通过 spring-boot 进行 Spring WS UsernameToken 身份验证 + 会话管理

Spring Boot JWT 身份验证

Spring Boot 端点未经过身份验证

使用 Spring Boot/Spring Security 对 LDAP 进行证书身份验证

Spring Boot 安全登录验证失败