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

问题描述 投票:0回答:1

我一般来说是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。我仍然对为什么使用错误的格式会引发该特定消息引发异常感到困惑。

spring spring-boot spring-webflux illegalstateexception
1个回答
0
投票

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

简短的答案是使用@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中创建问题吗?

© www.soinside.com 2019 - 2024. All rights reserved.