javaspring-bootresthttpjasperserver

Spring Boot - Rest templates seems to ignore accept header set through the HttpEntity


I am making a call to one of the Jasper server API endpoints and I have to set the header "Accept" to "application/json" for the service to return a JSON response. I have validated the API from Postman -

Request from Postman

When I try to simulate the same behavior from my Spring Boot rest client, I try to set the accept header to 'application/json' but Spring seems to ignore the same and adds the accept header as shown below -

Rest template debug output

I have validated the same by enabling DEBUG for rest template using the following parameter - logging.level.org.springframework.web.client.RestTemplate=DEBUG

Below is the code snippet for my rest client -

HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setBasicAuth(serviceUsername, servicePassword, StandardCharsets.UTF_8);

ResponseEntity<String> response = null;

String url = serviceEndpoint + "?reportUnitURI="
                    + URLEncoder.encode(reportPath, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20")
                    + "&label=" + URLEncoder.encode(label, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20");
            LOGGER.info("URL : " + url);

HttpEntity<String> requestEntity = new HttpEntity<String>("",
                    headers);

response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);

Solution

  • Even though the code sets the accept header, the HttpMessageConverter used my scenario (StringHttpMessageConverter) does not allow changes on the accept headers and maintains the default values.

    To get past that I have modified the StringHttpMessageConverter behavior at runtime to allow setting the preferred accept header.

    @Bean
    public RestTemplate getRestTemplate() {
        final RestTemplate restTemplate = new RestTemplate();
        final List<HttpMessageConverter<?>> converters = new ArrayList<>();
        
        for (HttpMessageConverter converter : restTemplate.getMessageConverters()) {
             if (converter instanceof StringHttpMessageConverter) {
                 ((StringHttpMessageConverter) converter).setWriteAcceptCharset(true);
                 ((StringHttpMessageConverter) converter).setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_JSON));
                 converters.add(converter);
             }
         }
        
        restTemplate.setMessageConverters(converters);
        return restTemplate;
    }
    

    Now, the framework allows my REST client to modify the header, code snippet below -

    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
    headers.setBasicAuth(serviceUsername, new String(new DecryptionService().decrypt(servicePassword)), StandardCharsets.UTF_8);
    
    ResponseEntity<String> response = null;
    
    try {
        String url = serviceEndpoint + "?reportUnitURI=" + reportPath + "&label=" + processLabel(label, false);
        HttpEntity<String> requestEntity = new HttpEntity<String>("",
                        headers);
    
        response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class); 
        //more stuff