Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@RequestBody with Mono argument throws 400 BAD_REQUEST "Request body is missing:" [SPR-17463] #21995

Closed
spring-projects-issues opened this issue Nov 4, 2018 · 5 comments
Assignees
Labels
in: web status: invalid

Comments

@spring-projects-issues
Copy link
Collaborator

@spring-projects-issues spring-projects-issues commented Nov 4, 2018

Ralph Schaer opened SPR-17463 and commented

In a Spring Boot application with reactive web I have a POST endpoint with a request body of type Mono. This works fine with Spring Boot 2.0.6 (Spring 5.0.10). But when I upgraded to Spring Boot 2.1.0 (Spring 5.1.2) the application throws an error "Request body is missing"

 

To reproduce the problem create a Spring Boot 2.1.0 application with Spring Initializr and add "Reactive Web" as sole dependency. 

Then add a PostMapping method to the main class

@SpringBootApplication
@RestController
public class DemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
	
    @PostMapping("/register")
    public void register(@RequestBody Mono<String> token) {
        token.subscribe(System.out::println);
    }	
}

When you call this endpoint with curl the application throws an error

curl -v -d "token" -H "Content-Type: text/plain" http://localhost:8080/register
reactor.core.Exceptions$ErrorCallbackNotImplemented: org.springframework.web.server.ServerWebInputException: 400 BAD_REQUEST "Request body is missing: public void com.example.demo.DemoApplication.register(reactor.core.publisher.Mono<java.lang.String>)"
Caused by: org.springframework.web.server.ServerWebInputException: 400 BAD_REQUEST "Request body is missing: public void com.example.demo.DemoApplication.register(reactor.core.publisher.Mono<java.lang.String>)"
        at org.springframework.web.reactive.result.method.annotation.AbstractMessageReaderArgumentResolver.handleMissingBody(AbstractMessageReaderArgumentResolver.java:223) ~[spring-webflux-5.1.2.RELEASE.jar:5.1.2.RELEASE]
        at org.springframework.web.reactive.result.method.annotation.AbstractMessageReaderArgumentResolver.lambda$readBody$5(AbstractMessageReaderArgumentResolver.java:188) ~[spring-webflux-5.1.2.RELEASE.jar:5.1.2.RELEASE]

When you change the Spring Boot dependency to 2.0.6.RELEASE and run the same curl command you see that it works without any problems.


Affects: 5.1.2

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Nov 5, 2018

Brian Clozel commented

Hi Ralph Schaer,

I'm not sure we should consider that as a regression as I believe this case was never meant to work. The reference documentation says:

A method with a void, possibly asynchronous (for example, Mono<Void>), return type (or a null return value) is considered to have fully handled the response if it also has a ServerHttpResponse, a ServerWebExchange argument, or an @ResponseStatus annotation.

Generally, a void method return type means that by the time the controller method returns, the request/response exchange should be considered as processed. This means that WebFlux will close and release all HTTP resources. In your case, the incoming request is closed while the controller is still busy reading the incoming data.

I don't really know why this case was working previously, but maybe even in Spring Boot 2.0.6 a larger request body would break already.

A proper version of that Controller method would be:

@RestController
public class TestController {

  @PostMapping("/register")
  public Mono<Void> register(@RequestBody Mono<String> token) {
    return token.doOnNext(System.out::println).then();
  }
}

Now I don't know if we can clarify that part of the reference documentation even more; we could try and proactively reject those controller signatures, but it's not really something Spring usually does.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Nov 5, 2018

Ralph Schaer commented

Thanks a lot for the clarification. I changed the code with the proper implementation and it works fine now. 

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Nov 5, 2018

Rossen Stoyanchev commented

From a client perspective it is still a 200 response in both cases, but on the server side, as Brian pointed out, the void return value indicates handling is complete too soon, and there may not be enough time to read body. I confirmed the same behavior with 2.0.6 by increasing the body content.

@spring-projects-issues spring-projects-issues added status: invalid type: regression in: web labels Jan 11, 2019
@spring-projects-issues spring-projects-issues removed the type: regression label Jan 12, 2019
@pkgonan
Copy link

@pkgonan pkgonan commented Sep 11, 2021

@rstoyanchev @ralscha
Hi.
How did you solve this problem? I have same issue in spring framework 5.3.9 version.

Unable to execute HTTP request: 400 BAD_REQUEST "Request body is missing: public java.lang.Object 
    @PutMapping(value = ["/datas/{id}"])
    suspend fun create(
        @PathVariable @NotBlank id: String,
        @RequestBody data: Flow<ByteBuffer>
    ): ResponseEntity<Void> {
        val response = service.create(id, data)
        val resource = response.resource

        return ResponseEntity.ok()
            .header(HttpHeaders.ETAG, resource.eTag)
            .build()
    }

@ralscha
Copy link
Contributor

@ralscha ralscha commented Oct 25, 2021

@pkgonan I fixed it by using the correct return type. From public void register to public Mono<Void> register

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web status: invalid
Projects
None yet
Development

No branches or pull requests

4 participants