Question

I'm writing a simple client in Java to allow reusable use of proprietary virus scanning software accessible through a RESTful API. To upload a file for scanning the API requires a POST for Connect, followed by a POST for Publishing the file to the server. In the response to the Connect POST there are cookies set by the server which need to be present in the subsequent POST for publishing the file. I'm currently using Spring RestTemplate in my client.

My question is how do I access the cookies in the response to forward back to the server with the subsequent POST? I can see that they are present in the header that is returned but there are no methods on the ResponseEntity to access them.

Was it helpful?

Solution

RestTemplate has a method in which you can define Interface ResponseExtractor<T>, this interface is used to obtain the headers of the response, once you have them you could send it back using HttpEntity and added again.

 .add("Cookie", "SERVERID=c52");

Try something like this.

String cookieHeader = null;

new ResponseExtractor<T>(){
      T extractData(ClientHttpResponse response) {
        response.getHeaders();
      }
}

Then

  HttpHeaders headers = new HttpHeaders();
  headers.add("Cookie", cookieHeader );

  ResponseEntity<byte[]> response = restTemplate.exchange("http://example.com/file/123",
      GET,
      new HttpEntity<String>(headers),
      byte[].class);

Also read this post

OTHER TIPS

I've solved the problem by creating an interceptor which stores a cookie and puts it in next requests.

public class StatefulRestTemplateInterceptor implements ClientHttpRequestInterceptor {
    private String cookie;

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        if (cookie != null) {
            request.getHeaders().add(HttpHeaders.COOKIE, cookie);
        }
        ClientHttpResponse response = execution.execute(request, body);

        if (cookie == null) {
            cookie = response.getHeaders().getFirst(HttpHeaders.SET_COOKIE);
        }
        return response;
    }
}

Set the interceptor for your RestTemplate:

@Bean
public RestTemplate restTemplate(RestTemplateBuilder templateBuilder) {
    return templateBuilder
            .requestFactory(new BufferingClientHttpRequestFactory(new HttpComponentsClientHttpRequestFactory()))
            .interceptors(new StatefulRestTemplateInterceptor())
            .build();
}

Small update to handle sessions in a complete test with 'java.net.HttpCookie' Object.

@Thanks Shedon

import java.io.IOException;
import java.net.HttpCookie;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

/**
 * @link https://stackoverflow.com/questions/22853321/resttemplate-client-with-cookies
 */
@Component
public class RestTemplateWithCookies extends RestTemplate {

    private final List<HttpCookie> cookies = new ArrayList<>();

    public RestTemplateWithCookies() {
    }

    public RestTemplateWithCookies(ClientHttpRequestFactory requestFactory) {
        super(requestFactory);
    }

    public synchronized List<HttpCookie> getCoookies() {
        return cookies;
    }

    public synchronized void resetCoookies() {
        cookies.clear();
    }

    private void processHeaders(HttpHeaders headers) {
        final List<String> cooks = headers.get("Set-Cookie");
        if (cooks != null && !cooks.isEmpty()) {
            cooks.stream().map((c) -> HttpCookie.parse(c)).forEachOrdered((cook) -> {
                cook.forEach((a) -> {
                    HttpCookie cookieExists = cookies.stream().filter(x -> a.getName().equals(x.getName())).findAny().orElse(null);
                    if (cookieExists != null) {
                        cookies.remove(cookieExists);
                    }
                    cookies.add(a);
                });
            });
        }
    }

    @Override
    protected <T extends Object> T doExecute(URI url, HttpMethod method, final RequestCallback requestCallback, final ResponseExtractor<T> responseExtractor) throws RestClientException {
        final List<HttpCookie> cookies = getCoookies();

        return super.doExecute(url, method, new RequestCallback() {
            @Override
            public void doWithRequest(ClientHttpRequest chr) throws IOException {
                if (cookies != null) {
                    StringBuilder sb = new StringBuilder();
                    for (HttpCookie cookie : cookies) {
                        sb.append(cookie.getName()).append(cookie.getValue()).append(";");
                    }
                    chr.getHeaders().add("Cookie", sb.toString());
                }
                requestCallback.doWithRequest(chr);
            }

        }, new ResponseExtractor<T>() {
            @Override
            public T extractData(ClientHttpResponse chr) throws IOException {
                processHeaders(chr.getHeaders());
                return responseExtractor.extractData(chr);
            }
        });
    }

}

You need to use exchange method of RestTemplate of Java Spring framework.

Read this tutorial: http://codeflex.co/java-rest-client-get-cookie/

It can be achieved automatically like what browsers do with the below code.

Reference: 1. https://hc.apache.org/httpclient-3.x/cookies.html

  1. https://hc.apache.org/httpcomponents-client-ga/tutorial/html/statemgmt.html
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
    RequestConfig requestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT).build();        
    HttpClient  httpClient = HttpClientBuilder.create()
            .setDefaultRequestConfig(requestConfig)         
            .build();   
    
    RestTemplate restTemplate = restTemplateBuilder
            .requestFactory(
                    () -> {
                        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
                        requestFactory.setHttpClient(httpClient);
                        return new BufferingClientHttpRequestFactory(requestFactory);
                    })
            .basicAuthentication("username", "password")
            .build();
    
    return restTemplate;
}

i've wrote a simple class that extends RestTemplate and handles cookies.

import java.io.IOException;
import java.net.URI;
import java.util.List;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

public class RestTemplateWithCookies extends RestTemplate {

    private List<String> cookies = null;

    public RestTemplateWithCookies() {
    }

    public RestTemplateWithCookies(ClientHttpRequestFactory requestFactory) {
        super(requestFactory);
    }

    private synchronized List<String> getCoookies() {
        return cookies;
    }

    private synchronized void setCoookies(List<String> cookies) {
        this.cookies = cookies;
    }

    public synchronized void resetCoookies() {
        this.cookies = null;
    }

    private void processHeaders(HttpHeaders headers) {
        final List<String> cookies = headers.get("Set-Cookie");
        if (cookies != null && !cookies.isEmpty()) {
            setCoookies(cookies);
        }
    }

    @Override
    protected <T extends Object> T doExecute(URI url, HttpMethod method, final RequestCallback requestCallback, final ResponseExtractor<T> responseExtractor) throws RestClientException {
        final List<String> cookies = getCoookies();

        return super.doExecute(url, method, new RequestCallback() {
            @Override
            public void doWithRequest(ClientHttpRequest chr) throws IOException {
                if(cookies != null) {
                    for(String cookie : cookies) {
                        chr.getHeaders().add("Cookie", cookie);
                    }
                }
                requestCallback.doWithRequest(chr);
            }

        }, new ResponseExtractor<T>() {
            @Override
            public T extractData(ClientHttpResponse chr) throws IOException {
                processHeaders(chr.getHeaders());
                return responseExtractor.extractData(chr);
            }
        });
    }

}

To get more browser like behavior you can use this interceptor:

import java.io.IOException;
import java.net.HttpCookie;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;

public class CookieHandlingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {

  private static final Logger LOG = LoggerFactory.getLogger(CookieHandlingClientHttpRequestInterceptor.class);

  private final Map<String, HttpCookie> cookies = new HashMap<>();

  @Override
  public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
    List<String> cookiesForRequest = cookies.values().stream()
        .filter(cookie -> cookie.getPath() != null && request.getURI().getPath().startsWith(cookie.getPath()))
        .map(HttpCookie::toString)
        .collect(Collectors.toList());
    LOG.info("Using cookies: {}", cookiesForRequest);
    request.getHeaders().addAll(HttpHeaders.COOKIE, cookiesForRequest);

    ClientHttpResponse response = execution.execute(request, body);

    List<String> newCookies = response.getHeaders().get(HttpHeaders.SET_COOKIE);
    if (newCookies != null) {
      List<HttpCookie> parsedCookies = newCookies.stream().flatMap(rawCookie -> HttpCookie.parse(HttpHeaders.SET_COOKIE + ": " + rawCookie).stream()).collect(Collectors.toList());
      LOG.info("Extracted cookies from response: {}", parsedCookies);
      parsedCookies.forEach(newCookie -> cookies.put(newCookie.getName(), newCookie));
    }

    return response;
  }
}

And keep in mind that by default RestTemplate follows redirects for GET requests. In this case the above interceptor is bypassed.

Code snippet of RestTemplate GET Call with Cookie

        final String url = "url";
        String set_cookie = "cookie value";

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.add("Cookie", set_cookie);
        HttpEntity request = new HttpEntity(headers);
        
        ResponseEntity<Profile> response = restTemplate.exchange(url, HttpMethod.GET, request, Profile.class);
                    package zuulx;

                import java.net.URI;
                import java.net.URISyntaxException;

                import org.springframework.http.HttpEntity;
                import org.springframework.http.HttpHeaders;
                import org.springframework.http.HttpMethod;
                import org.springframework.http.ResponseEntity;
                import org.springframework.web.client.RestTemplate;

                public class CookieTest {
                /**
                 * 
                 * array(1) {
                  ["aaa"]=>
                  string(2) "11"
                }


                 * @param args
                 * @throws URISyntaxException
                 */
                    public static void main(String[] args) throws URISyntaxException {
                        
                        HttpHeaders headers = new HttpHeaders();
                          headers.add("Cookie", "aaa=11" );
                          
                        
                           RestTemplate restTemplate = new RestTemplate();
                           
                    //  URI url= new URI("http://localhost:9088/cktest");
                    //  System.out.println( restTemplate.getForObject(url, String.class));  
                        
                          String url = "http://localhost/showck.php";
                          url="http://localhost:9088/cktest";
                        ResponseEntity response = restTemplate.exchange(url,
                                  HttpMethod.GET,
                                  new HttpEntity (headers) ,String.class);
                          
                          System.out.println(response.getBody());
                        
                        
                        //http://localhost/showck.php

                    }

                }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top