英文:
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 = "/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");
}
}
If I query with a payload such as:
{
"important": 42,
"something": "value"
}
Everything is working perfectly fine, very happy.
However, if there is a typo: (note the typo on "important")
{
"importantWithTypo": 42,
"something": "value"
}
Or the required "important" is absent (note the JSON is not even complete)
{
"something": "value"
}
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 = "/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");
}
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<String> 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
已经提供的答案解决了这个问题。
但是我想详细说明一下你所问的一件事情。
如何在这种情况下失败
"importantWithTypo": 42,
有两个方面。
- 如果没有提供所需字段,您希望返回4XX错误(这可以通过已经给出的答案实现)--
@NonNull
/@NonEmpty
与@Validate
注解结合使用。 - 您希望在出现未知字段
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
"importantWithTypo": 42,
2 aspects to it.
- 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 - You want to error out on presence of an unknown field
importantWithTypo
. This can be achieved by jackson'sfail_on_unknown_properties
property. (May be default isfail_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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论