Thursday 8 June 2017

Using RestTemplate to handle Http to Https Redirect

What is a RestTemplate? 

For the beginners of spring who don't know what a  RestTemplate api is for , this api comes as part of the spring-mvc module and it provides you client utilities to enable  HTTP/HTTPS communication to servers using the REST protocol. The methods provided by RestTemplate closely follows the REST principal.

 Problem Statement :

Suppose we are trying to perform a GET request to a specific URL and the URL returns a redirect response with a location header. 

For example :

GET /index.html HTTP/1.1
Server responds like this : 
HTTP/1.1 302 Found
Location: http://www.assortedminds.com/samples

How do you handle this using RestTemplate?
By default, RestTemplate redirects GET requests automatically. The default SimpleClientHttpRequestFactory of RestTemplate takes care of this redirect if the request is of GET type. But if the request internally redirects from one protocol to another, lets say from HTTP to HTTPS, the redirect doesn't happen by default. On a more serious note, this is not a feature of RestTemplate. RestTemplate internally uses the native JAVA Apis for handling http connections, so this behaviour derives from HttpURLConnection apis.
So We need to handle the redirect code manually in such cases.

The below code snippet is a sample of how we can handle such redirects.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public String fetchSomeDataFromURL(String url) {

        //GET request to perform Rest Call
        ResponseEntity<String> response = perFormRestCall (url);

        //handle Redirects since HttpUrlConnection by design wont automatically redirect from Htpp to Https
        HttpHeaders httpHeaders = response.getHeaders();
        HttpStatus statusCode = response.getStatusCode();
        if (statusCode.equals(HttpStatus.MOVED_PERMANENTLY) || statusCode.equals(HttpStatus.FOUND) || statusCode.equals(HttpStatus.SEE_OTHER)) {
            if (httpHeaders.getLocation() != null) {
                response = perFormRestCall (httpHeaders.getLocation().toString());
            } else {
                throw new RuntimeException();
            }
        }

        return response.getBody();

    }

private ResponseEntity<String> perFormRestCall(String url) {
        try {
            return restTemplate.getForEntity(url.trim(), String.class);
        } catch (Exception ex) {
            throw new RuntimeException();
        }
    }
 

Notes: 

For 301(Moved permanently), 302 (Found) or 303(See other) responses, there should be a mandatory location header.
More details can be found in this link:

 https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html