Springboot – 验证 @RequestBody

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

Springboot - validate @RequestBody

问题

问题:是否有可能在不特定编写条件语句的情况下,验证请求体的JSON有效负载?也许可以通过注释或配置来实现?

我有一个非常简单的POJO:

public class Foo {

    private int important;
    private String something;

    // 构造函数,getter,setter,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"
}

一切都运行得非常完美,非常满意。

然而,如果出现了拼写错误:(注意 "important" 处的拼写错误)

{
    "importantWithTypo": 42,
    "something": "value"
}

或者缺少了必需的 "important"(注意 JSON 甚至没有完整):

{
    "something": "value"
}

请求和计算仍然是有效的!而 "important" 的值为 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 Boot 的配置?

谢谢

英文:

Question: It is possible to validate the JSON payload of a request body, without specifically writing if statements? Maybe via annotation or configuration?

I have a very easy POJO:

public class Foo {

    private int important;
    private String something;

//constructors, getter, seters, toString
}

And a very easy controller class:

@SpringBootApplication
@RestController
public class QuestionController {

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

    @GetMapping(value = &quot;/question&quot;)
    Mono&lt;String&gt; question(@RequestBody Foo foo) {
        System.out.println(&quot;The object foo, with value for important = &quot; + foo.getImportant() + &quot; and something = &quot; + foo.getSomething());
	    return Mono.just(&quot;question&quot;);
    }

}

If I query with a payload such as:

{
    &quot;important&quot;: 42,
    &quot;something&quot;: &quot;value&quot;
}

Everything is working perfectly fine, very happy.

However, if there is a typo: (note the typo on "important")

{
    &quot;importantWithTypo&quot;: 42,
    &quot;something&quot;: &quot;value&quot;
}

Or the required "important" is absent (note the JSON is not even complete)

{
    &quot;something&quot;: &quot;value&quot;
}

The request and computation are still valid! And the value of "important" is 0!

I do not want Spring to default to 0 and to thinks everything is fine.

I also do not want to change my types from primitives to boxed object.

Without me writing something like:

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

What is the most efficient way to resolve this? Some kind of annotation? Or maybe Spring boot configuration?

Thank you

答案1

得分: 37

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

Mono<String> question(@Valid @RequestBody Foo foo) {
    ...
}
public class Foo {

    @NotNull
    private Integer important;
    private String something;

    // 构造函数、getter、setter、toString
}

更多信息请参阅:https://lmonkiewicz.medium.com/the-power-of-spring-rest-api-validation-77be83edef

英文:

Add @NotNull annotation on a field (you may need to change type to Integer), and add @Valid annotation on the method parameter of the controller.

Mono&lt;String&gt; question(@Valid @RequestBody Foo foo) {
    ...
}
public class Foo {

    @NotNull
    private Integer important;
    private String something;

//constructors, getter, seters, toString
}

You can find more information here: https://lmonkiewicz.medium.com/the-power-of-spring-rest-api-validation-77be83edef

答案2

得分: 7

已经提供的答案解决了这个问题。

但是我想详细说明一下你所问的一件事情。

如何在这种情况下失败
&quot;importantWithTypo&quot;: 42,

有两个方面。

  1. 如果没有提供所需字段,您希望返回4XX错误(这可以通过已经给出的答案实现)-- @NonNull/@NonEmpty@Validate 注解结合使用。
  2. 您希望在出现未知字段 importantWithTypo 时出错。这可以通过 jackson 的 fail_on_unknown_properties 属性实现。(也许默认值是 fail_on_unknown_properties = enabled,我没有检查过所以不确定)。

不要做第二件事。这将使您的两个服务紧密耦合。通过设置 fail_on_unknown_properties = enabled,您放弃了以非破坏性的方式增强消费者/调用方服务的机会。然后,如果您在实体中引入新属性,您将不得不协调两个应用程序的发布,以使消费者发布保持不变,直到生产者/提供者发布为止。回滚也是同样的情况,这次是相反的顺序。

英文:

The already provided answer addresses the question.

However I would like to elaborate on one thing that you asked.

How to fail on this
&quot;importantWithTypo&quot;: 42,

2 aspects to it.

  1. you want to return a 4XX if a required field is not present (this can be achieved by the already given answer) -- @NonNull/@NonEmpty in conjunction with @Validate annotation
  2. You want to error out on presence of an unknown field importantWithTypo. This can be achieved by jackson's fail_on_unknown_properties property. (May be default is fail_on_unknown_properties = enabled, I haven't checked so not sure).

Don't do this 2nd thing. This will make your 2 services tightly coupled. By doing this fail_on_unknown_properties = enabled, you are forfeiting the opportunity to enhance the consumer/caller service in a nonbreaking manner. Then, if you are introducing a new attribute in the entity, you will have to coordinate both the apps releases in such a manner that consumer release will be held until producer/provider makes it. Same goes for rollback, this time in reverse order.

huangapple
  • 本文由 发表于 2020年10月25日 04:04:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/64517537.html
匿名

发表评论

匿名网友

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

确定