80

I have a RESTful API I'm trying to connect with via Android and RestTemplate. All requests to the API are authenticated with HTTP Authentication, through setting the headers of the HttpEntity and then using RestTemplate's exchange() method.

All GET requests work great this way, but I cannot figure out how to accomplish authenticated POST requests. postForObject and postForEntity handle POSTs, but have no easy way to set the Authentication headers.

So for GETs, this works great:

HttpAuthentication httpAuthentication = new HttpBasicAuthentication("username", "password");
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAuthorization(httpAuthentication);

HttpEntity<?> httpEntity = new HttpEntity<Object>(requestHeaders);

MyModel[] models = restTemplate.exchange("/api/url", HttpMethod.GET, httpEntity, MyModel[].class);

But POSTs apparently don't work with exchange() as it never sends the customized headers and I don't see how to set the request body using exchange().

What is the easiest way to make authenticated POST requests from RestTemplate?

Nick Daugherty
  • 2,763
  • 4
  • 20
  • 17
  • 1
    requestHeaders.setAuthorization(httpAuthentication).. its undefined..no method such as setAuthorization.. – deadend Sep 12 '17 at 09:00

4 Answers4

144

Ok found the answer. exchange() is the best way. Oddly the HttpEntity class doesn't have a setBody() method (it has getBody()), but it is still possible to set the request body, via the constructor.

// Create the request body as a MultiValueMap
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();     

body.add("field", "value");

// Note the body object as first parameter!
HttpEntity<?> httpEntity = new HttpEntity<Object>(body, requestHeaders);

ResponseEntity<MyModel> response = restTemplate.exchange("/api/url", HttpMethod.POST, httpEntity, MyModel.class);
Hans Wouters
  • 588
  • 5
  • 16
Nick Daugherty
  • 2,763
  • 4
  • 20
  • 17
  • 2
    It throws a TypeMismatch error Type mismatch: cannot convert from ResponseEntity to MyModel .. I guess it should be ResponseEntity model = restTemplate.exchange("/api/url", HttpMethod.POST, httpEntity, MyModel.class); – Sid Apr 07 '16 at 10:19
  • `MyModel myModel = restTemplate.exchange(completeServiceUrl, HttpMethod.GET, request, MyModel.class).getBody()` will help solve the problem that you mentioned – isank-a Aug 31 '17 at 16:07
  • 2
    THANKS. This costed me two hours, until I stumbled over your post. – guenhter Jan 08 '18 at 15:26
  • 1
    For this to work in recent Spring Security environments you MUSTdisable CSRF-Protection like http.csrf().disable() – M46 Jul 23 '18 at 17:53
29

Slightly different approach:

MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>();
headers.add("HeaderName", "value");
headers.add("Content-Type", "application/json");

RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());

HttpEntity<ObjectToPass> request = new HttpEntity<ObjectToPass>(objectToPass, headers);

restTemplate.postForObject(url, request, ClassWhateverYourControllerReturns.class);
Andrew
  • 36,676
  • 11
  • 141
  • 113
  • 4
    Where does ObjectToPass come from – Ismail Iqbal Oct 18 '16 at 09:53
  • 3
    @Ismail Iqbal - it can be any object with info you need to pass (for example private Person john = Person("John", 24);). It will be converted to json payload and sent to the server via request body – Andrew Oct 18 '16 at 10:38
12

I was recently dealing with an issue when I was trying to get past authentication while making a REST call from Java, and while the answers in this thread (and other threads) helped, there was still a bit of trial and error involved in getting it working.

What worked for me was encoding credentials in Base64 and adding them as Basic Authorization headers. I then added them as an HttpEntity to restTemplate.postForEntity, which gave me the response I needed.

Here's the class I wrote for this in full (extending RestTemplate):

public class AuthorizedRestTemplate extends RestTemplate{

    private String username;
    private String password;

    public AuthorizedRestTemplate(String username, String password){
        this.username = username;
        this.password = password;
    }

    public String getForObject(String url, Object... urlVariables){
        return authorizedRestCall(this, url, urlVariables);
    }

    private String authorizedRestCall(RestTemplate restTemplate, 
            String url, Object... urlVariables){
        HttpEntity<String> request = getRequest();
        ResponseEntity<String> entity = restTemplate.postForEntity(url, 
                request, String.class, urlVariables);
        return entity.getBody();
    }

    private HttpEntity<String> getRequest(){
        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", "Basic " + getBase64Credentials());
        return new HttpEntity<String>(headers);
    }

    private String getBase64Credentials(){
        String plainCreds = username + ":" + password;
        byte[] plainCredsBytes = plainCreds.getBytes();
        byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes);
        return new String(base64CredsBytes);
    }
}
MMascarin
  • 468
  • 4
  • 6
zacran
  • 855
  • 6
  • 18
8

Very useful I had a slightly different scenario where I the request xml was itself the body of the POST and not a param. For that the following code can be used - Posting as an answer just in case anyone else having similar issue will benefit.

    final HttpHeaders headers = new HttpHeaders();
    headers.add("header1", "9998");
    headers.add("username", "xxxxx");
    headers.add("password", "xxxxx");
    headers.add("header2", "yyyyyy");
    headers.add("header3", "zzzzz");
    headers.setContentType(MediaType.APPLICATION_XML);
    headers.setAccept(Arrays.asList(MediaType.APPLICATION_XML));
    final HttpEntity<MyXmlbeansRequestDocument> httpEntity = new HttpEntity<MyXmlbeansRequestDocument>(
            MyXmlbeansRequestDocument.Factory.parse(request), headers);
    final ResponseEntity<MyXmlbeansResponseDocument> responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity,MyXmlbeansResponseDocument.class);
    log.info(responseEntity.getBody());
Soumya
  • 1,054
  • 2
  • 16
  • 31