6

I am trying to format my response content type on a @RestController between json and xml. It seems to work in the case of Accept header or path extension (.json,.xml). My application is packaged as a WAR and deployed to a tomcat instance. However I am facing 2 problems currently.

1) Even though I set my default content type to JSON on ContentNegotiationConfigurer a request like curl -X GET -H "Cache-Control: no-cache" http://localhost:8080/v1/api/version resolves to XML

2) When I use the param on the request the content resolves incorrectly. On accessing curl -X GET -H "Cache-Control: no-cache" http://localhost:8080/v1/api/version?format=json I get XML back.

SpringBoot Application

@EnableAutoConfiguration
@ComponentScan
@Configuration
@EnableWebMvc
public class Application extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder     builder) {
        builder.sources(Application.class);
        return super.configure(builder);
    }
}

WebMvcConfigurerAdapter

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {    

    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;    

    private CustomObjectMapper objectMapper;    

    @PostConstruct
    public void init() {
        List<HttpMessageConverter<?>> messageConverters = requestMappingHandlerAdapter.getMessageConverters();
        for (HttpMessageConverter<?> messageConverter : messageConverters) {
            if (messageConverter instanceof MappingJackson2HttpMessageConverter) {
                MappingJackson2HttpMessageConverter m = (MappingJackson2HttpMessageConverter) messageConverter;
                m.setObjectMapper(objectMapper);
            }
        }
    }    

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer
                .defaultContentType(MediaType.APPLICATION_JSON)
                .favorParameter(true)
                .useJaf(false)
                .mediaType("xml", MediaType.APPLICATION_XML)
                .mediaType("json", MediaType.APPLICATION_JSON);
        super.configureContentNegotiation(configurer);
    }    


    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
        mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper);
        converters.add(mappingJackson2HttpMessageConverter);    

        converters.add(createXmlHttpMessageConverter());
        super.configureMessageConverters(converters);
    }    

    private HttpMessageConverter<Object> createXmlHttpMessageConverter() {
        MarshallingHttpMessageConverter xmlConverter =
                new MarshallingHttpMessageConverter();    

        XStreamMarshaller xstreamMarshaller = new XStreamMarshaller();
        xmlConverter.setMarshaller(xstreamMarshaller);
        xmlConverter.setUnmarshaller(xstreamMarshaller);
        return xmlConverter;
    }    

    @Autowired
    public void setRequestMappingHandlerAdapter(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
        this.requestMappingHandlerAdapter  = requestMappingHandlerAdapter;
    }    

    @Autowired
    public void setObjectMapper(CustomObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }
}

Rest Controller

@RestController
@RequestMapping("/v1/api")
public class RestVersionController {    

    @ResponseStatus(HttpStatus.OK)
    @RequestMapping(value = "/version", method = RequestMethod.GET)
    public
    @ResponseBody
    ApiVersion getVersion() {
        return new ApiVersion(1);
    }    

    @JsonIgnoreProperties(ignoreUnknown = true)
    @XmlRootElement
    public static class ApiVersion {
        int version;
        String currentVersion = "Current version is ";    

        public ApiVersion() {
        }    

        public ApiVersion(int version) {
            this.version = version;
            this.currentVersion = this.currentVersion + version;
        }    

        public ApiVersion(int version, String currentVersion) {
            this.version = version;
            this.currentVersion = currentVersion;
        }    

        public int getVersion() {
            return version;
        }    

        public void setVersion(int version) {
            this.version = version;
        }    

        public String getCurrentVersion() {
            return currentVersion;
        }    

        public void setCurrentVersion(String currentVersion) {
            this.currentVersion = currentVersion;
        }
    }
}

Code @ Github sample

Any help is appreciated, Thanks!

  • 3
    First ditch your `CustomObjectMapper` and all the complex configuration stuff and add the `spring.jackson.property-naming-strategy=CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES` to your `application.properties` to achieve the same result. Why do you have a JAXB annotation but use XStream? Basically remove all your configuration (including the `@EnableWebMvc`) and only leave the `configureContentNegotiation`, restart (or redeploy). You are doing to much, work with the framework not against it. You can also remove `@ResponseBody` from your controller, that is implied by `@RestController`. – M. Deinum Apr 30 '15 at 13:09
  • Thanks @M.Deinum removing the extra configuration fixed the problem. However I still have to annotate my pojo with the JAXB annotation, I get the following error in the logs if I don't ` Requested media type is 'application/xml' (based on parameter 'format'='xml') Resolving exception from handler [public ad.rws.campaign.v1.controller.RestVersionController.getVersion()]: org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation` Latest code at [https://github.com/cyril-thomas/content-test] – cyril.thomas Apr 30 '15 at 19:50
  • My bad, fixed the problem. Thanks again @M.Deinum – cyril.thomas Apr 30 '15 at 20:35

0 Answers0