如何在Spring Webflux中使用@RequestBody并避免IllegalStateException?

ace*_*123 4 spring illegalstateexception spring-boot spring-webflux

一般来说,我对 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);
    }

}

Run Code Online (Sandbox Code Playgroud)

如下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;
  }
}
Run Code Online (Sandbox Code Playgroud)

但是,当我发送 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$1(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]
Run Code Online (Sandbox Code Playgroud)

问题似乎是我@RequestBody直接使用标签而不是通过访问表单数据ServerWebExchange。但是,我在网上看到了多个@RequestBody与 WebFlux 一起使用的示例,没有任何问题。比如Spring本身的框架文档

使用标签会产生类似的问题IllegalStateExceptions(如此),但具有不同的异常消息。

我使用了 spring-boot 初始化程序来创建项目的等级文件,但我自己没有更改它。

有人知道问题是什么以及我该如何解决它吗?

编辑

多亏了 Aviad Levy,我才找到了问题所在。也就是说,POST 请求的格式不正确,因为正文是在 url 中编码发送的。为了使其正常工作,我必须确保请求正文是 JSON 对象,并且Content-Type标头设置为application/json. 我只是仍然困惑为什么使用错误的格式会引发该特定消息的异常。

Ros*_*hev 5

更新:此答案假设您想要发布表单数据,这是基于错误消息的假设。

简短的答案是使用@ModelAttribute. 此外,您不能阻塞,除非切换线程(例如通过publishOn()运算符),但在这里您不必这样做。这有效:

@PostMapping("/test")
public Mono<Person> processPerson(@ModelAttribute Mono<Person> personMono){
    return personMono.doOnNext(p -> p.setAge(42));
}
Run Code Online (Sandbox Code Playgroud)

长答案是@RequestBody仅支持MultiValueMap<String,String>表单数据。要将表单数据绑定到对象上,您需要使用@ModelAttribute. 然而,在这种情况下,您看到的错误消息是关于不同的内容。它试图防止主体被消耗两次,一次通过访问表单参数的代码exchange.getFormData(),第二次通过声明控制器方法参数。因此,错误消息仍然适用,但前提是您尝试绑定到MultiValueMap.

还有改进的空间。至少,错误消息可以得到改善。此外,@RequestBody对表单数据的支持还可以得到改进,因为显然您想要做的事情是可能的,并且您不必记住切换注释。

您介意在https://github.com/spring-projects/spring-framework/issues中创建一个问题吗?