I'm using Google Translate API with Spring RestTemplate in my application and it works fine until I use GET http request. However if I heve large piece of data the service will return 414 error (Request-URI Too Large).
So I decided to use POST (according to the note).
And here is my code:
String content = "q=Hello";
HttpHeaders headers = new HttpHeaders();
headers.set("X-HTTP-Method-Override", "GET");
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<String> request = new HttpEntity<String>(content, headers);
String response = new RestTemplate.postForObject(
"https://www.googleapis.com/language/translate/" +
"v2?key=<my_key>&source=en&target=ru", request, String.class);
The service returns error 400 bad request and I have no idea why. I have successfully created similar code using jQuery so I know this way should work.
Please help me to fix the problem.
I think the way you form URI is incorrect and thats why you get 400. Looking at the google translate API documentation, it looks like they expect 'q' as URI query parameter. Also you seem to be doing a POST request for a GET. From google translate API docs
GET https://www.googleapis.com/language/translate/v2?key=INSERT-YOUR-KEY&source=en&target=de&q=Hello%20world
Try this,
Map<String, String> queryParameters = new HashMap<String, String>();
queryParameters.put("key","my_key_here");
queryParameters.put("source","en");
queryParameters.put("target","ru");
queryParameters.put("q","Hello World");
String url = "https://www.googleapis.com/language/translate/" +
"v2?key={key}&source={source}&target={target}&q={q}";
HttpHeaders headers = new HttpHeaders();
headers.set("X-HTTP-Method-Override", "GET");
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<String> request = new HttpEntity<String>(null, headers);
RestTemplate restclient = new RestTemplate();
String response=restclient.getForObject(url,request,String.class,queryParameters);
Related
I have been trying to upload an image to Cloudflare images, but no matter what I try it gets rejected with "400 Bad Request: "ERROR 5400: Bad request: Error parsing form fields""
https://api.cloudflare.com/#cloudflare-images-upload-an-image-using-a-single-http-request
I am currently using SpringBoot with Rest Templates, here's an example of what I have tried so far:
public String uploadImage(File file) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new ByteArrayHttpMessageConverter());
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + API_TOKEN);
LinkedMultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
//body.add("\"file\"", new FileSystemResource(file));
body.add("file", new FileSystemResource(file));
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(body, headers);
ResponseEntity<String> response = restTemplate.exchange(URL, HttpMethod.POST, request, String.class);
return response.toString();
}
I have tried using different forms of the file, changing the parameters, using different methods to make the request, ex: restTemplate.exchange vs restTemplate.getForObject, etc... and I have had no luck. If anyone can help me out or point me in the right direction, I would be greatly appreciated!
I implemented a KeyCloak client with the following configuration:
keycloak configuration
And I implemented my callback endpoint like that:
#GetMapping("/callback")
#ResponseBody
public String getToken(#RequestParam String code) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", MediaType.APPLICATION_FORM_URLENCODED.toString());
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("code", code);
map.add("client_id", "spring-login-app");
map.add("client_secret", "");
map.add("grant_type", "authorization_code");
map.add("redirect_uri", UriComponentsBuilder.fromHttpUrl("http://127.0.0.1:3002/callback").build().toString());
HttpEntity formEntity = new HttpEntity<MultiValueMap<String, String>>(map, headers);
try {
ResponseEntity<KeycloakTokenResponse> response =
restTemplate.exchange("http://127.0.0.1:8080/auth/realms/raroc/protocol/openid-connect/token",
HttpMethod.POST,
formEntity,
KeycloakTokenResponse.class);
KeycloakTokenResponse resp = response.getBody();
return resp.getAccess_token();
} catch (Exception e) {
System.out.println(e.getMessage());
}
return "nothing";
}
The problem is when I tried to get my access token from this callback endpoint, I received a 400 Bad Request error with the following message: 400 Bad Request: "{"error":"invalid_grant","error_description":"Incorrect redirect_uri"}"
When I test it through postman with the same x-www-form-url-encoded form params, it works fine, but in spring, it's impossible to do it.
I tried many scenario for the "redirect_uri" param, just a String, an UriComponentsBuilder.formHttpUrl, some other URL encoder thing but unfortunately I still have this error.
You can try to specify a: http://localhost:3002/* instead of your actual redirect URI in the KeyCloak configuration but from what I read in your settings, everything looks good.
Be careful also sometimes if you are changing the configuration of Keyloak, you need to restart it to take the changes into account.
If you want to test also a full scenario, open an incognito tab with your browser, and it should work.
I'm trying to revoke google token following API ("Content-type:application/x-www-form-urlencoded", request type POST, https://accounts.google.com/o/oauth2/revoke?token={token}).
My code example is:
private static final String REVOKE_URL = "https://accounts.google.com/o/oauth2/revoke";
private static final String TOKEN = "token";
#Autowired
private RestTemplate restTemplate;
public void revokeToken(TokenDetailsDto tokenDetailsDto) {
HttpHeaders headers = new HttpHeaders();
headers.add(CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
HttpEntity<?> entity = new HttpEntity(headers);
Map<String, String> parameters = new HashMap<>();
parameters.put(TOKEN, tokenDetailsDto.getRefreshToken());
LOGGER.info("used parameters:\n\turl={},\n\tentity={},\n\turiParameters={}", REVOKE_URL, entity, uriParameters);
restTemplate.exchange(REVOKE_URL, HttpMethod.GET, entity, Void.class, parameters);
}
result is exception:
17:30:54,323 INFO # com.nextiva.calendar.client.google.GoogleRevokeAccessService.revokeToken used parameters:
url=https://accounts.google.com/o/oauth2/revoke,
entity=<{Content-Type=[application/x-www-form-urlencoded]}>,
uriParameters={token=1/mzMH8VPrltarCo3LOHU_tBWi4qK5e20uIM0R_Al9T34}
17:31:20,718 ERROR # com.nextiva.calendar.web.error.handling.BaseExceptionHandler.exception 400 Bad Request
org.springframework.web.client.HttpClientErrorException: 400 Bad Request
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:63)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:700)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:653)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:621)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:540)
I try use rest client to handle request like in documentation it is ok (using rest client I tried to use POST request with specified content type).
Same error result if I use
restTemplate.postForEntity(REVOKE_URL, entity, Void.class, uriParameters);
So issue is in restTemplate using (my spring part). Is any ideas, what I've done wrong here?
Solution is:
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
HttpEntity<?> entity = new HttpEntity(headers);
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(REVOKE_URL)
.queryParam("token", tokenDetailsDto.getRefreshToken());
LOGGER.info("used parameters:\n\turl={},\n\tentity={},\n\turiParameters={}", REVOKE_URL, entity);
restTemplate.postForObject(builder.build().encode().toUri(), entity, Void.class);
Small explanation: service expect urlParameters, but parameters was not urlParameters, its request parameters. So I used UriComponentsBuilder to build correct request.
I've the following request with curl that talks to Microsoft Azure services without a problem.
curl --request POST https://login.microsoftonline.com/common/oauth2/v2.0/token --data 'client_id=fe37...06-566f5c762ab2&grant_type=authorization_code&client_secret=tPv..dQfqomaG&scope=mail.read&code=OAQABAAIA...gAA'
Here is the java code that is throwing Bad Request exception:
public String getToken(String authCode){
try {
HttpHeaders headers = new HttpHeaders();
String url = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
headers.add("client_id", "fe3..b2");
headers.add("client_secret", "tP..aG");
headers.add("grant_type", "authorization_code");
headers.add("code", authCode);
headers.add("scope", "mail.read");
HttpEntity<?> entity = new HttpEntity<>(headers);
RestTemplate restTemplate = new RestTemplate();
HttpEntity<String> response = restTemplate.exchange(builder.build().toUri(), HttpMethod.POST, entity, String.class);
}
catch (Exception e){
e.printStackTrace();
}
return null;
}
I've also tried adding the --data section in to parameters object and I receive the same problem. I am using RestTemplate but I am open for other suggestions.
I appericiate your help.
I suppose that problem is that in curl example you pass these parameters inside POST body, while in your java code you use headers instead. Try change it to usage of body params of entity object:
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
body.add("client_id", "fe3..b2");
// ... rest params
// Note the body object as first parameter!
HttpEntity<?> entity = new HttpEntity<Object>(body, new HttpHeaders());
You need to send these parameters in the request entity formatted as form url encoded and also set the content-type to application/x-www-form-urlencoded.
Your body can be a string (according to your example):
String data = "client_id=fe37...06-566f5c762ab2&grant_type=authorization_code&client_secret=tPv..dQfqomaG&scope=mail.read&code=OAQABAAIA...gAA";
HttpEntity<String> entity = new HttpEntity<>(data);
Set a content type header:
headers.add("Content-Type", "application/x-www-form-urlencoded");
(Actual implementation depends on the library you use)
I need to send a POST HTTP request using Robospice for Android that will have data in the following format:
Content-Type: application/json
{'var1':'var1val','telNumber':'var2val'}
There may be other HTTP headers inside but this is the only header required by the server - I've tested it with a simple HelloWorld request that doesn't require any specific objects in the body.
The problem is that I can't send request body in the right format. I'm doing everything according the Robospice tutorial.
loadDataFromNetwork() method
public T loadDataFromNetwork() throws Exception { // I'm making generic requests, T is a class representing given request
Uri.Builder uriBuilder = Uri.parse(mUrl).buildUpon(); // mURL is just a full URL, including the method (just HTTP, not HTTPS)
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
for (NameValuePair nvp : mPostParams) { // From the constructor: mPostParams = new ArrayList<NameValuePair>();
body.add(nvp.getName(), nvp.getValue());
}
HttpEntity<?> requestEntity = new HttpEntity<Object>(body, mHttpHeaders);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<T> responseEntity = getRestTemplate().exchange(
uriBuilder.build().toString(), HttpMethod.POST, requestEntity,
mClazz);
return responseEntity.getBody();
}
createRestTemplate() in the JsonSpiceService class (which extends SpringAndroidSpiceService)
public RestTemplate createRestTemplate() {
RestTemplate restTemplate = new RestTemplate(true);
MappingJacksonHttpMessageConverter jsonConverter = new MappingJacksonHttpMessageConverter();
FormHttpMessageConverter formHttpMessageConverter = new
FormHttpMessageConverter();
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
final List<HttpMessageConverter<?>> listHttpMessageConverters = restTemplate
.getMessageConverters();
setTimeout(restTemplate);
listHttpMessageConverters.add(jsonConverter);
listHttpMessageConverters.add(formHttpMessageConverter);
listHttpMessageConverters.add(stringHttpMessageConverter);
restTemplate.setMessageConverters(listHttpMessageConverters);
return restTemplate;
}
Doing it this way, I keep getting 500 Internal Server Error and I am pretty sure it is related to format of the body. Is there any way of getting a raw HTTP request (not the toString() version of some methods just a raw request)? What else can I be doing wrong - can it be about encoding of the body?
Note that I was testing the very same request with both Chrome app for sending REST requests and curl (worked fine).
You are getting confused in your SpringAndroid usage. Look at the docs of HttpEntity, you will see that you use a MultiValueMap as the Body. In that case, you are right, body.toString is called.
MultiValueMap is used in the way you think it is only for headers. For the body, HttpEntity expects a Pojo basically, and thus considers you MultiValueMap as a Pojo.
A simple workaround would be to create by hand the string you want to post as a body :
String body = "key=value&key=value&key=value"
This argument could be used as the first parameter of the constructor of HttpEntity.