【问题标题】:How to use @RequestBody in Spring Webflux and avoid IllegalStateException?如何在 Spring Webflux 中使用 @RequestBody 并避免 IllegalStateException?
【发布时间】:2020-03-29 11:59:48
【问题描述】:

总的来说,我是 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$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]

问题似乎是我直接使用@RequestBody 标签而不是通过ServerWebExchange 访问表单数据。但是,我在网上看到了多个示例,其中 @RequestBody 与 WebFlux 一起使用没有任何问题。例如the framework documentation of Spring itself

在使用标签产生IllegalStateExceptions(如here)的情况下似乎存在类似问题,但有不同的异常消息。

我使用spring-boot初始化器创建项目的成绩文件,但我自己没有更改。

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

编辑

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

【问题讨论】:

  • 您能否将导致此错误的请求添加到您的帖子中?
  • @AviadLevy 您可能已经看到了它,但错误确实在发布请求中。我编辑了我的帖子来解释这个问题。
  • 很高兴读到你想通了。
  • @RequestBody Mono&lt;Person&gt; personMono 导致您的错误,您正在接受 Mono&lt;Person&gt; 当您发送带有请求的正文并且您没有指定 Content-Type 时,spring 默认假设它是 form您正在发送的数据,因为您正在发布带有正文的内容。因此,当它尝试调用 ServerWebExchange.getFormData() 时,您会得到一个非法状态异常,并且该函数会因该异常而崩溃。

标签: spring spring-boot spring-webflux illegalstateexception


【解决方案1】:

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

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

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

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

还有改进的余地。至少,可以改进错误消息。此外,@RequestBody 对表单数据的支持可以得到改进,因为很明显你想要做的事情是可能的,你不应该记住切换注释。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-26
    • 1970-01-01
    • 2019-10-06
    • 2023-02-11
    • 2022-01-01
    • 2015-02-18
    • 2019-03-03
    • 1970-01-01
    相关资源
    最近更新 更多