Spring RestTemplate HTTP Post with parameters cause 400 bad request error
Asked Answered
D

2

24

Possible duplicate Need help on RestTemplate Post Request with Body Parameters? and Spring RESTtemplate POST but these answers don't work for me
I tried to get access token from Instagram API by Spring Android. The request from Instagram 's document like this:

curl \-F 'client_id=CLIENT-ID' \
-F 'client_secret=CLIENT-SECRET' \
-F 'grant_type=authorization_code' \
-F 'redirect_uri=YOUR-REDIRECT-URI' \
-F 'code=CODE' \https://api.instagram.com/oauth/access_token

Here is my request access token (After I get request token successful):

 MultiValueMap<String, String> mvm = new LinkedMultiValueMap<String, String>();
    mvm.add("client_id", INSTAGRAM_CILENT_ID);
    mvm.add("client_secret", INSTAGRAM_SECRET);
    mvm.add("grant_type", "authorization_code");
    mvm.add("redirect_uri", CALLBACKURL);
    mvm.add("code", requestToken);

    InstagramResult result = restTemplate .postForObject("https://api.instagram.com/oauth/access_token", mvm, InstagramResult .class);

The result mapping class:

public class InstagramLogin {
   public String access_token;
   public InstagramUser user;
}

public class InstagramUser {
   public String id;
   public String username;
   public String full_name;
   public String profile_picture;
}

And the rest template:

   RestTemplate restTemplate = new RestTemplate();
    final List<HttpMessageConverter<?>> listHttpMessageConverters = new ArrayList< HttpMessageConverter<?> >(); 

    listHttpMessageConverters.add(new GsonHttpMessageConverter());
    listHttpMessageConverters.add(new FormHttpMessageConverter());
    listHttpMessageConverters.add(new StringHttpMessageConverter());
    restTemplate.setMessageConverters(listHttpMessageConverters);

But I always get 400 bad request error. Here is my stack trace:

04-03 09:32:45.366: W/RestTemplate(31709): POST request for "https://api.instagram.com/oauth/access_token" resulted in 400 (BAD REQUEST); invoking error handler
04-03 09:32:46.857: E//DefaultRequestRunner.java:138(31709): 09:32:46.862 Thread-32787 An exception occurred during request network execution :400 BAD REQUEST
04-03 09:32:46.857: E//DefaultRequestRunner.java:138(31709): org.springframework.web.client.HttpClientErrorException: 400 BAD REQUEST
04-03 09:32:46.857: E//DefaultRequestRunner.java:138(31709):    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:76)
04-03 09:32:46.857: E//DefaultRequestRunner.java:138(31709):    at org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate.java:524)
04-03 09:32:46.857: E//DefaultRequestRunner.java:138(31709):    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:481)
04-03 09:32:46.857: E//DefaultRequestRunner.java:138(31709):    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:439)
04-03 09:32:46.857: E//DefaultRequestRunner.java:138(31709):    at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:317)

P/s: I am surely my parameter values are right, because I tested by curl and it worked fine.

Diet answered 3/4, 2014 at 2:45 Comment(2)
why not just a HashMap?Preside
Since curl works and the code posted doesn't. If you can't find a solution on here I'd run requests from both your code and curl through fiddler. Take a look at the two requests and figure out what's different between them.Preside
N
28

A server will often return an HTTP 400 if the content type is not acceptable for a request. The curl example from instagram uses the -F parameter which specifies multipart post data:

-F, --form CONTENT  Specify HTTP multipart POST data (H)

Therefore, you may want to try explicitly setting the Content-type HTTP header in your RestTemplate request:

HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(mvm, requestHeaders);
ResponseEntity<InstagramResult> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, InstagramResult.class);
InstagramResult result = response.getBody();

As mentioned earlier in the comments, a proxy tool like fiddler can be really useful for debugging. The challenge with this situation is that you are working with SSL, so these tools won't be able to "see" the encrypted communications without special configuration.

Nitid answered 3/4, 2014 at 16:34 Comment(1)
It helped me a lot , but still had the same response 400 Bad Request ... then I realized that the API returns that response even when they successfully processed your request but can not retrieve the code, not because you throw a bad request but because other reason like: " Matching code was not found or was already used" or "Redirect URI does not match registered redirect URI". From my point of view, they should return 200 OK response to these scenarios, although it was not the expected result, they successfully processed your request after all.Rouge
H
2
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(LINKEDIN_POST_URL);

List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
nameValuePairs.add(new BasicNameValuePair("grant_type", grant_type));
nameValuePairs.add(new BasicNameValuePair("code", code));
nameValuePairs.add(new BasicNameValuePair("redirect_uri", redirect_uri);
nameValuePairs.add(new BasicNameValuePair("client_id", client_id));
nameValuePairs.add(new BasicNameValuePair("client_secret", client_secret));

httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpclient.execute(httppost);

And to convert your response to String. You can use:

EntityUtils.toString(response.getEntity());

I suppose you want to send a post request for any object. For that a DefaultHttpClient is used. HttpPost Object will take the url as the argument (only url not the parameters). The parameters you want to send can be edited above in BasicNameValuePair object. First parameter as the name of the parameter and second would be its value. As soon as you call the execute method the response will come in the HttpResponse Object. You now have to convert the HttpResponse object to Entity. And from Entity you can call EntityUtils.toString() method to convert to String. I suppose you're expecting a json data. You might have to map a class corresponding to it to convert json data to java class object.

Hybridize answered 17/11, 2014 at 10:12 Comment(1)
Please make sure you add org.apache.httpcomponents dependecy in your project.Hybridize

© 2022 - 2024 — McMap. All rights reserved.