Spring Boot Webflux Handler errors when decoding XML request body content to Mono or Flux object

2.3k Views Asked by At

I have been trying to implement Spring reactive routers and handler instead of MVC controllers. Seemed simple enough, but my WebTestClient test has been failing with 500 Internal Server Error. Shorted code versoin of test.

@ContextConfiguration(classes = {FooRouter.class, FooServiceConfig.class, FoonHandler.class})
@WebFluxTest
public class FooHandlerTest {

@Test
public void testFooPost()  {
    webTestClient.post()
            .uri("/foot")
            .contentType(MediaType.APPLICATION_XML)
            .accept(MediaType.APPLICATION_XML)
            .body(Mono.just(foo), Foo.class)
            .exchange()
            .expectStatus().isCreated()
            .expectBody(Bar.class)
            .isEqualTo(bar);
}

During the test the router works fine and calls the handler. When the handler tries to create the Mono from the request body:

Mono<Foo> fooMono = request.bodyToMono(Foo.class);

that line kills the unit test and the WebTestClient receives a 500 status code. During debugging, although the mime-type is application/xml and it did map the Java class, the BodyExtractors is filtering the decoders and matches the class and mime type. For Json it is looking at the Java class and trying to get a mapping off of it before it compares the mime type. Deep within debug it crashes when it is still trying map the class to an object mapper.

I am currently using Spring Boot version 2.3.4-RELEASE with Java8. Documentation mentions if you have JaxB in your classpath then for XML it should use Jaxb2XmlDecoder and Endoder for default codecs. It will also use Jackson if you have that on your classpath. I do happen to have both minus the jackson-dataformat-xml dependency. I'm pretty sure it's either my test configuration or it can be WebFluxConfigurer, which I tried setting the default codec for the jaxb2Decoder and Encoder. I also did try adding the jackson-dataformat-xml dependency, but that did not work either. This is the webconfig I tried to use, but still crashing trying to decode mime-types of application/xml with the Json decoder. Here is the webconfig:

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {

    @Override
    public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
        configurer.registerDefaults(false);
        configurer.defaultCodecs().jaxb2Decoder(new Jaxb2XmlDecoder(MediaType.APPLICATION_XML));
        configurer.defaultCodecs().jaxb2Encoder(new Jaxb2XmlEncoder());
    }
}

I've been debugging and printed out the request objects message readers and the Jaxb2XmlEncoder is one of the codecs to the xml mime type. Here is the print message for JSON and XML Decoders:

Message Reader: org.springframework.http.codec.DecoderHttpMessageReader@5b2cea27
    DecoderHttpMessageReader Decoder: org.springframework.http.codec.json.Jackson2JsonDecoder@87b7472
        Mime Type: application/json
        Mime Type: application/*+json
Message Reader: org.springframework.http.codec.DecoderHttpMessageReader@198a86e1
    DecoderHttpMessageReader Decoder: org.springframework.http.codec.xml.Jaxb2XmlDecoder@50cb41d0
        Mime Type: application/xml
        Mime Type: text/xml
        Mime Type: application/*+xml

When in the readWithMessageReaders method in the BodyExtractors class there is a filter in the function that iterates through the message readers and decoders. When it gets to the DecoderHttpMessageReader,Jackson2JsonDecoder it starts to try and decode the request. Not sure why since the mime type in the debugger shows application/xml.

Does anyone have any suggestions on how to debug this piece of code:

Mono<Foo> fooMono = request.bodyToMono(Foo.class);

This is where it dies during Unit testing with the code above. I've tried debugging, but it will just stop the test when I get too deep. I just know it is going through the Java type when it is trying to match a codec. Most codecs just return right away, but the Json and XML decoders are trying to get the Java class attributes and are choking. The class is a JaxB class with all proper annotations. The WebTestClient has no problem encoding the class to XML for the test. So why can't the handler just convert the body to mono? Nothing is being thrown from that code statement.

1

There are 1 best solutions below

0
On BEST ANSWER

I knew it was something in my setup and unit testing was not showing the correct problem.

It was my return statement that was actually causing the error when getting the body content.

Old return statement:

return created(URI.create(response.getDocument().getPath()))
    .bodyValue(Mono.just(response));

New return statement:

return created(URI.create(response.getDocument().getPath()))
    .body(Mono.just(response), Response.class);

See the difference with bodyValue and just body? That caused caused the request body not being able to convert to a Mono.