Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I extract the request body in a Spring reactive handler-function?

I am implementing a handler function with Spring web flux and want to extract the values of an x-www-form-urlencoded POST-Body.

public Mono<ServerResponse> handle(ServerRequest incomingHttpRequest) {
    Mono<MultiValueMap<String, String>> formData = incomingHttpRequest.body(BodyExtractors.toFormData());
    return formData
            .doOnEach(signal -> {LOG.info("EACH: " + signal.getType());})
            .doOnSubscribe(x -> {LOG.info("SUBSCRIBED");})
            .doOnNext(next -> {LOG.info("NEXT: " + next);})
            .doOnError(x -> {LOG.info("ERROR: " + x.getMessage());})
            .doOnSuccess(x -> {LOG.info("SUCCESS: " + x);})
            .flatMap(multiValueMap -> ServerResponse
                .badRequest()
                .body(BodyInserters.fromObject(
                    "searchTerm: " + multiValueMap.getFirst("searchTerm")
                ))
            );
}

The handler is added to the router like:

return RouterFunctions.route(RequestPredicates.POST("/mysearch"), searchHandler::handle);

Sending a POST with Body searchTerm=something&lang=de via Postman gets me the following result:

o.s.w.s.adapter.HttpWebHandlerAdapter    : [f405c02b] HTTP POST "/mysearch"                                                                               
o.s.http.codec.FormHttpMessageReader     : [f405c02b] Read form fields [searchTerm, lang] (content masked)                                                
o.s.w.r.f.s.s.RouterFunctionMapping      : [f405c02b] Mapped to de.myproject...
de.myproject.MyHandler                   : SUBSCRIBED                                                                                                     
de.myproject.MyHandler                   : EACH: onComplete                                                                                               
de.myproject.MyHandler                   : SUCCESS: null                                                                                                  
o.s.w.s.adapter.HttpWebHandlerAdapter    : [f405c02b] Completed 200 OK                                                                                    

I would expect to get a BAD_REQUEST containing the searchTerm. But I get a 200 instead.

To me, it looks like the resulting Mono is subscribed and then completes successfully. But formData never receives a value and thus never enters the flatMap(...)

What am I doing wrong mapping the Mono<MultiValueMap<String, String>> to a Mono<ServerResponse>?

like image 369
groovedigga Avatar asked Nov 01 '25 14:11

groovedigga


1 Answers

There are two things at play here:

  • the fact that you're only allowed to subscribe (i.e. read) the request body once
  • getting the form data from the request requires reading and parsing it

In Spring Boot 2.0.x-2.1.x, the HiddenHttpMethodFilter is enabled by default. This filter is useful for using HTTP methods that aren't natively supported by browsers in HTML forms (like "DELETE"). This filter needs to parse the form data if the incoming request is of that type.

Arguably, this use case is less and less common and it's been disabled by default as of Spring Boot 2.2.

Now in your sample, reading and parsing the incoming request won't work since it's been already parsed by the filter. Disabling the filter will work around that problem.

But a better way to solve that is to use the dedicated method request.formData(), which will parse and cache the result (and return the already parsed data if called multiple times).

You can read more about it in the Spring Framework reference documentation.

like image 156
Brian Clozel Avatar answered Nov 04 '25 05:11

Brian Clozel



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!