Making authenticated POST requests with Spring RestTemplate for Android

236,122

Solution 1

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);

Solution 2

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);

Solution 3

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);
    }
}

Solution 4

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());
Share:
236,122
Nick Daugherty
Author by

Nick Daugherty

Updated on July 05, 2022

Comments

  • Nick Daugherty
    Nick Daugherty almost 2 years

    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?

  • Sid
    Sid about 8 years
    It throws a TypeMismatch error Type mismatch: cannot convert from ResponseEntity<MyModel> to MyModel .. I guess it should be ResponseEntity<MyModel> model = restTemplate.exchange("/api/url", HttpMethod.POST, httpEntity, MyModel.class);
  • Ismail Iqbal
    Ismail Iqbal over 7 years
    Where does ObjectToPass come from
  • Andrew
    Andrew over 7 years
    @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
  • 2280259
    2280259 over 6 years
    MyModel myModel = restTemplate.exchange(completeServiceUrl, HttpMethod.GET, request, MyModel.class).getBody() will help solve the problem that you mentioned
  • guenhter
    guenhter over 6 years
    THANKS. This costed me two hours, until I stumbled over your post.
  • M46
    M46 almost 6 years
    For this to work in recent Spring Security environments you MUSTdisable CSRF-Protection like http.csrf().disable()