How to use @RequestBody in Spring Webflux and avoid IllegalStateException?

acester123 :

I'm new to Webflux and Spring in general and I've been having troubles setting a simple server that handles a POST request:

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);
    }

}

With Person being a follows:

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;
  }
}

However, when I send the POST request, I get an java.lang.IllegalStateException. More specifically here's the top of stack trace:

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]

The problem seems to be that I'm using the @RequestBody tag directly instead of accessing the form data via ServerWebExchange. However, I've seen multiple examples online where @RequestBody was used together with WebFlux without any issues. For example the framework documentation of Spring itself.

There seems to have been similar issues where using the tag produces IllegalStateExceptions (like here), but with different exception's message.

I used the spring-boot initializer for creating the grade file of the project, but have not changed it myself.

Does anybody have any idea what the problem is and how I could solve it?

EDIT

Thanks to Aviad Levy I was able to find the problem. Namely, the POST request was not properly formatted, as the body was being sent encoded in the url. To make it work, I had to make sure the request body was a JSON object and the Content-Type header was set to application/json. I'm just still confused as to why using the wrong format throws the exception with that particular message.

Rossen Stoyanchev :

Update: this answer assumes that you want to post form data, which was the assumption based on the error message.

The short answer is to use @ModelAttribute. In addition you can't block, not without switching threads (e.g. via publishOn() operator), but here you don't have to do that. This works:

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

The long answer is that @RequestBody supports only MultiValueMap<String,String> for form data. For binding form data onto an Object you need to use @ModelAttribute. In this case however the error message you're seeing is about something different. It is trying to prevent the body from being consumed twice, once via code that access form parameters via exchange.getFormData() and a second time by declaring a controller method parameter. So the error message would still apply but only if you were trying to bind to MultiValueMap.

There is room for improvement. In the very least, the error message can be improved. Additionally @RequestBody support for form data could be improved since clearly what you're trying to do is possible and you shouldn't have to remember to switch out annotations.

Would you mind creating an issue in https://github.com/spring-projects/spring-framework/issues?

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

How to use Jaeger with Spring WebFlux?

Spring security : How to use @RolesAllowed with @RequestBody

Is @RequestBody should be wrapped with Mono Publisher in Spring Webflux?

How to use WebSession in Spring WebFlux to persist data?

how to use spring webflux for file streaming

How to use Hystrix with Spring WebFlux WebClients?

Spring WebFlux. How to get the request body in two different formats using @RequestBody annotation?

How to use Spring WebSessionIdResolver with Spring Security 5 in a Spring webflux application?

@JsonView how to use with @RequestBody

javax.json.JsonPatch unsupported as @RequestBody in Spring Webflux Microservice

Kotlin Spring Boot Webflux validate @RequestBody with @Valid annotation

Spring WebFlux - how to get data from DB to use in the next step

Spring webflux: how to use custom jackson ObjectWriter in ResponseEntityResultHandler?

How to use OpenApi annotations in spring-webflux RouterFunction endpoints?

How to use Mono and Flux in handler functions with Spring WebFlux Reactive ways

How to configure Spring Boot 2 WebFlux to use SSL?

Spring boot webflux application won't start IllegalStateException

How to use @RequestBody and @RequestParam together

Use Spring @RestController without implicit @RequestBody

Spring Controller: use domain objects as @RequestBody

How to get locale in Spring WebFlux?

How to set ViewResolver in Spring WebFlux

How to return 404 with Spring WebFlux

How to use "Functional bean definition Kotlin DSL" with Spring Boot and Spring WebFlux?

spring boot webflux: avoid thread-blocking method calls in handler

Which CoroutineScope to use for Spring Boot WebFlux endpoint

How to send @Requestbody and @Requestpart together in spring

How to get the @RequestBody in an @ExceptionHandler (Spring REST) 2.0

How to bind JSON hasmasp to the @RequestBody in spring boot