Search code examples
javaxmlspring-bootspring-webfluxjackson-dataformat-xml

SpringBoot Webflux cannot return application/xml


In my reactive REST API I'm trying to return an XML response. However, I always get a JSON, namely 406 NOT_ACCEPTABLE. Any idea why?

@RestController
@RequestMapping(path = "/xml", produces = APPLICATION_XML_VALUE)
public class RestApi {
    @GetMapping(path = "/get")
    public Publisher<ResponseEntity> get() {
        return Mono.just(ResponseEntity.ok().contentType(APPLICATION_XML).body(new Datta("test")));
    }

    @PostMapping(path = "/post", consumes = APPLICATION_XML_VALUE)
    public Publisher<ResponseEntity<Datta>> post(@RequestBody Datta datus) {
        datus.setTitle(datus.getTitle() + "!");
        return Mono.just(ResponseEntity.ok().contentType(APPLICATION_XML).body(datus));
    }
}

java.lang.AssertionError: Expected :application/xml Actual :application/json;charset=UTF-8

plugins {
    id 'org.springframework.boot' version '2.1.3.RELEASE'
    id "io.spring.dependency-management" version "1.0.7.RELEASE"
}
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
    implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.8"
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

These are the links to my REST controller and a unit test. Thanks!


Solution

  • Apparently, jackson-dataformat-xml does not yet support XML Marshalling in WebFlux. As for now I see two possibilities:

    1. Either add org.springframework.boot:spring-boot-starter-web on the classpath (there should be both starter-web and starter-webflux). However, this will only work with Servlet 3.1 runtimes (e.g. Tomcat).
    2. Or if you want a fully fledged reactive web server (e.g. Netty) use xml marshalling from JAXB (Jaxb2XmlEncoder and Jaxb2XmlDecoder):

    build.gradle:

    sourceCompatibility = '11'
    
    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-webflux'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
    
        // Java 11 removed these Java EE modules
        implementation "javax.xml.bind:jaxb-api:2.3.1"
        implementation "com.sun.xml.bind:jaxb-core:2.3.0.1"
        implementation "com.sun.xml.bind:jaxb-impl:2.3.2"
    
        compileOnly "org.projectlombok:lombok"
        annotationProcessor "org.projectlombok:lombok"
    }
    

    POJO:

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @XmlRootElement
    public class Datta {
        private String title;
    }
    

    Mind the 3 javax.xml.bind dependencies (you don't need these for Java 8) and the @XmlRootElement annotation. This solution works right away, however if you want further customization, implement your own WebFluxConfigurer:

    @Configuration
    @EnableWebFlux
    public class WebConfig implements WebFluxConfigurer {
        @Override
        public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
            configurer.registerDefaults(false);
            configurer.customCodecs().decoder(new Jaxb2XmlDecoder());   // <- here
            configurer.customCodecs().encoder(new Jaxb2XmlEncoder());   // <- here
    
        }
    }
    

    Here is the link to the source code.