如何在Spring Webflux 中使用@RequestBody 并避免IllegalStateException?
How to use @RequestBody in Spring Webflux and avoid IllegalStateException?
我是 Webflux 的新手,一般来说 Spring 我在设置一个处理 POST 请求的简单服务器时遇到了麻烦:
WebfluxtestApplication.java
package com.test.webfluxtest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@SpringBootApplication
@RestController
public class WebfluxtestApplication {
public static void main(String[] args) {
SpringApplication.run(WebfluxtestApplication.class, args);
}
@PostMapping
public Mono<Person> processPerson(@RequestBody Mono<Person> personMono){
Person person = personMono.block();
person.setAge(42);
return Mono.just(person);
}
}
其中 Person
如下:
Person.java
package com.test.webfluxtest;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Person {
private String name;
private int age;
public Person(@JsonProperty String name, @JsonProperty int age){
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return this.name + " " + this.age;
}
}
但是,当我发送 POST 请求时,我收到 java.lang.IllegalStateException
。更具体地说,这里是堆栈跟踪的顶部:
java.lang.IllegalStateException: In a WebFlux application, form data is accessed via ServerWebExchange.getFormData().
at org.springframework.web.reactive.result.method.annotation.AbstractMessageReaderArgumentResolver.readBody(AbstractMessageReaderArgumentResolver.java:158) ~[spring-webflux-5.2.1.RELEASE.jar:5.2.1.RELEASE]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ? HTTP POST "/" [ExceptionHandlingWebHandler]
Stack trace:
at org.springframework.web.reactive.result.method.annotation.AbstractMessageReaderArgumentResolver.readBody(AbstractMessageReaderArgumentResolver.java:158) ~[spring-webflux-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.web.reactive.result.method.annotation.AbstractMessageReaderArgumentResolver.readBody(AbstractMessageReaderArgumentResolver.java:126) ~[spring-webflux-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.web.reactive.result.method.annotation.RequestBodyMethodArgumentResolver.resolveArgument(RequestBodyMethodArgumentResolver.java:64) ~[spring-webflux-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:123) ~[spring-webflux-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.web.reactive.result.method.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:200) ~[spring-webflux-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.web.reactive.result.method.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:137) ~[spring-webflux-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter.lambda$handle(RequestMappingHandlerAdapter.java:200) ~[spring-webflux-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44) ~[reactor-core-3.3.0.RELEASE.jar:3.3.0.RELEASE]
问题似乎是我直接使用了 @RequestBody
标签,而不是通过 ServerWebExchange
访问表单数据。但是,我在网上看到了多个示例,其中 @RequestBody
与 WebFlux 一起使用没有任何问题。例如 the framework documentation of Spring itself.
使用标签生成 IllegalStateExceptions
(如 here)似乎也存在类似问题,但异常消息不同。
我使用 spring-boot 初始值设定项来创建项目的成绩文件,但我自己没有更改它。
有没有人body知道问题是什么以及我该如何解决?
编辑
多亏了 Aviad Levy,我才得以找到问题所在。
也就是说,POST 请求的格式不正确,因为 body 是在 url 中编码发送的。为了让它工作,我必须确保请求 body 是 JSON object 并且 Content-Type
header 设置为 application/json
。
我仍然对为什么使用错误的格式会引发该特定消息的异常感到困惑。
更新:这个答案假设你想post形成数据,这是基于错误信息的假设。
简短的回答是使用 @ModelAttribute
。此外,您不能阻塞,而不是不切换线程(例如通过 publishOn()
运算符),但在这里您不必那样做。这有效:
@PostMapping("/test")
public Mono<Person> processPerson(@ModelAttribute Mono<Person> personMono){
return personMono.doOnNext(p -> p.setAge(42));
}
长答案是 @RequestBody
仅支持 MultiValueMap<String,String>
表单数据。要将表单数据绑定到对象上,您需要使用 @ModelAttribute
。但是,在这种情况下,您看到的错误消息是关于不同内容的。它试图防止主体被消耗两次,一次是通过 exchange.getFormData()
访问表单参数的代码,第二次是通过声明控制器方法参数。因此错误消息仍然适用,但前提是您尝试绑定到 MultiValueMap
.
还有改进的余地。至少,可以改进错误消息。此外,@RequestBody
可以改进对表单数据的支持,因为显然您尝试做的事情是可能的,并且您不必记住切换注释。
您介意在 https://github.com/spring-projects/spring-framework/issues 中创建一个问题吗?
我是 Webflux 的新手,一般来说 Spring 我在设置一个处理 POST 请求的简单服务器时遇到了麻烦:
WebfluxtestApplication.java
package com.test.webfluxtest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@SpringBootApplication
@RestController
public class WebfluxtestApplication {
public static void main(String[] args) {
SpringApplication.run(WebfluxtestApplication.class, args);
}
@PostMapping
public Mono<Person> processPerson(@RequestBody Mono<Person> personMono){
Person person = personMono.block();
person.setAge(42);
return Mono.just(person);
}
}
其中 Person
如下:
Person.java
package com.test.webfluxtest;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Person {
private String name;
private int age;
public Person(@JsonProperty String name, @JsonProperty int age){
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return this.name + " " + this.age;
}
}
但是,当我发送 POST 请求时,我收到 java.lang.IllegalStateException
。更具体地说,这里是堆栈跟踪的顶部:
java.lang.IllegalStateException: In a WebFlux application, form data is accessed via ServerWebExchange.getFormData().
at org.springframework.web.reactive.result.method.annotation.AbstractMessageReaderArgumentResolver.readBody(AbstractMessageReaderArgumentResolver.java:158) ~[spring-webflux-5.2.1.RELEASE.jar:5.2.1.RELEASE]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ? HTTP POST "/" [ExceptionHandlingWebHandler]
Stack trace:
at org.springframework.web.reactive.result.method.annotation.AbstractMessageReaderArgumentResolver.readBody(AbstractMessageReaderArgumentResolver.java:158) ~[spring-webflux-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.web.reactive.result.method.annotation.AbstractMessageReaderArgumentResolver.readBody(AbstractMessageReaderArgumentResolver.java:126) ~[spring-webflux-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.web.reactive.result.method.annotation.RequestBodyMethodArgumentResolver.resolveArgument(RequestBodyMethodArgumentResolver.java:64) ~[spring-webflux-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:123) ~[spring-webflux-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.web.reactive.result.method.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:200) ~[spring-webflux-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.web.reactive.result.method.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:137) ~[spring-webflux-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter.lambda$handle(RequestMappingHandlerAdapter.java:200) ~[spring-webflux-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44) ~[reactor-core-3.3.0.RELEASE.jar:3.3.0.RELEASE]
问题似乎是我直接使用了 @RequestBody
标签,而不是通过 ServerWebExchange
访问表单数据。但是,我在网上看到了多个示例,其中 @RequestBody
与 WebFlux 一起使用没有任何问题。例如 the framework documentation of Spring itself.
使用标签生成 IllegalStateExceptions
(如 here)似乎也存在类似问题,但异常消息不同。
我使用 spring-boot 初始值设定项来创建项目的成绩文件,但我自己没有更改它。
有没有人body知道问题是什么以及我该如何解决?
编辑
多亏了 Aviad Levy,我才得以找到问题所在。
也就是说,POST 请求的格式不正确,因为 body 是在 url 中编码发送的。为了让它工作,我必须确保请求 body 是 JSON object 并且 Content-Type
header 设置为 application/json
。
我仍然对为什么使用错误的格式会引发该特定消息的异常感到困惑。
更新:这个答案假设你想post形成数据,这是基于错误信息的假设。
简短的回答是使用 @ModelAttribute
。此外,您不能阻塞,而不是不切换线程(例如通过 publishOn()
运算符),但在这里您不必那样做。这有效:
@PostMapping("/test")
public Mono<Person> processPerson(@ModelAttribute Mono<Person> personMono){
return personMono.doOnNext(p -> p.setAge(42));
}
长答案是 @RequestBody
仅支持 MultiValueMap<String,String>
表单数据。要将表单数据绑定到对象上,您需要使用 @ModelAttribute
。但是,在这种情况下,您看到的错误消息是关于不同内容的。它试图防止主体被消耗两次,一次是通过 exchange.getFormData()
访问表单参数的代码,第二次是通过声明控制器方法参数。因此错误消息仍然适用,但前提是您尝试绑定到 MultiValueMap
.
还有改进的余地。至少,可以改进错误消息。此外,@RequestBody
可以改进对表单数据的支持,因为显然您尝试做的事情是可能的,并且您不必记住切换注释。
您介意在 https://github.com/spring-projects/spring-framework/issues 中创建一个问题吗?