I have a problem when Consume Rest Service with RestTemplate in Desktop App whereas the problem doesn't appear when i use in Web app.
This Is the Debugging logs
15:30:40.448 [main] DEBUG o.s.web.client.RestTemplate - Reading [java.util.List] as "application/json" using [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter#98adae2]
15:30:40.452 [main] DEBUG httpclient.wire.content - << "[{"name":"Indonesia","id":1},{"name":"AlaySia","id":2},{"name":"Autraliya","id":3}]"
Exception in thread "main" java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.mgm.domain.Country
And this is the Code that i use.
String url = "http://localhost:8080/mgm/country";
List<MediaType> mediaTypes = new ArrayList<MediaType>();
mediaTypes.add(MediaType.APPLICATION_JSON);
HttpHeaders headers = new HttpHeaders();
headers.setAccept(mediaTypes);
HttpEntity<Country> httpEntity = new HttpEntity<Country>(null, headers);
try {
ResponseEntity<List> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, List.class);
List<Country> countries = responseEntity.getBody();
System.out.println(countries.get(0).getName());
} catch (RestClientException exception) {
exception.printStackTrace();
}
Code above doesn't give errors when i place it in web app. I use Spring Rest MVC to Provide JSON and Consume it with RestTemplate.
I think there is a problem when Jackson Convert java.util.LinkedHashMap to Country . it Seems that countries.get(0) actually has LinkedHashMap type not Country and problem will appeared when i invoke one of Country methode like .getName()
Try using an array instead:
String url = "http://localhost:8080/mgm/country";
List<MediaType> mediaTypes = new ArrayList<MediaType>();
mediaTypes.add(MediaType.APPLICATION_JSON);
HttpHeaders headers = new HttpHeaders();
headers.setAccept(mediaTypes);
HttpEntity<Country> httpEntity = new HttpEntity<Country>(null, headers);
try {
ResponseEntity<Country[]> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, Country[].class);
Country[] countries = responseEntity.getBody();
System.out.println(countries[0].getName());
} catch (RestClientException exception) {
exception.printStackTrace();
}
Related
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.
Still cracking my brain after having made several calls to retrieve Token and Product, posting a Product was almost impossible with restTemplate even though it succeeded using Postman REST client.
public AddProductResponse createProduct(#NonNull AuthenticationTokenResponse authenticationTokenResponse)
{
String url = authenticationTokenResponse.getInstance_url() + "/services/data/v39.0/sobjects/Product2";
HttpHeaders headers = new HttpHeaders();
//headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.set("Authorization", "Bearer " + authenticationTokenResponse.getAccess_token());
headers.setContentType(MediaType.APPLICATION_JSON);
//headers.add("Authorization", "Bearer " + authenticationTokenResponse.getAccess_token());
MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<>();
requestBody.add("Name", "Product Name");
requestBody.add("product_Reference__c", "SOMERef");
HttpEntity<?> requestEntity = new HttpEntity<>(requestBody, headers);
RestTemplate restTemplate = new RestTemplate();
try
{
ResponseEntity<AddProductResponse> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, AddProductResponse.class);
return response.getBody();
}
catch (Exception e)
{
LOG.debug("Error creating product");
e.printStackTrace();
}
return null;
}
I wonder why my postman call succeeded but not with Spring RestTemplate.
POstman screenshot.
postman body.
{"Name" : "My New Prod ",
"product_Reference__c": "REFAKINTEST3"}
In my Android app I try to make a GET request via restTemplate.exchange but it leads very often into a 400 error very seldom it's a 200 response.
GET request for "http://someURL/items/modified/2018-12-20T12%253A47%253A43%252B01%253A00" resulted in 400 (); invoking error handler
org.springframework.web.client.HttpClientErrorException: 400
I tried to do the request with encoded and decoded parameter but it's the same problem. The only thing what changes is the timestamp in the request. I don't think it's a backend problem, because I did a couple requests via Swagger and Postman on the same interface and all of them worked without a problem. I also tried to update spring-android to version 2.0.0.M3 but still the same problem.
String url = ServiceAppConstants.HOSTNAME + ServiceAppConstants.REST_ITEMS_MODIFIED + URLEncoder.encode(lastSynchronisationDate);
try {
HttpEntity<String> httpEntity = RestServiceUtils.getHttpEntity(context);
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
// runs in the error here
ResponseEntity<ArrayList> response = restTemplate.exchange(url, HttpMethod.GET, httpEntity, ArrayList.class);
items= response.getBody();
items = mapper.convertValue(items, new TypeReference<List<Items>>(){});
} catch (RestClientException e) {
/* do stuff */
}
to set the token
#NonNull
public static HttpEntity<String> getHttpEntity(Context context) {
UserStorage userStorage = new UserStorage(context);
HttpHeaders headers = new HttpHeaders();
try {
String token = userStorage.getJsonWebToken();
headers.set(ServiceAppConstants.HEADER_SECURITY_TOKEN, token);
}catch (Exception ex){
Log.e(RestServiceUtils.class.getName(), "Could not get json web token", ex);
}
return new HttpEntity<String>("parameters", headers);
}
This is how the request looks like in the android profiler
This is how the request looks like if it's send by swagger
use new HttpEntity(headers); (without "parameters")
the "parameters" string is the request body according to HttpEntity documentation
that might caused the problem.
I am not able to call Spring RestTemplate with HttpEntity for POST request.
My call to RestTemplate gives Base64 string in Postman but using my java implementation it gives following error:
java.lang.IllegalArgumentException: Illegal base64 character 5b
at java.util.Base64$Decoder.decode0(Base64.java:714)
at java.util.Base64$Decoder.decode(Base64.java:526)
at java.util.Base64$Decoder.decode(Base64.java:549)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
My implementation is:
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
final HttpEntity<String> request = new HttpEntity<String>(searchRequestInput, headers);
final ResponseEntity<String> postForEntity = restTemplate
.postForEntity(baseURL, request, String.class);
String response = postForEntity.getBody();
I have tried following solutions, but it didn't work
here
and this
Also I have refered this
You can use HttpMessageConverter to request your restTemplate call for HttpEntity.
Which can read and write Strings from the HTTP request and response.
From doc: By default, this converter supports all text media types (text/*), and writes with a Content-Type of text/plain.
You can try this by implementing StringHttpMessageConverter as below:
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
final HttpEntity<String> request = new HttpEntity<String>(searchRequestInput, headers);
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
messageConverters.add(new StringHttpMessageConverter());
restTemplate.setMessageConverters(messageConverters);
then call restTemplate with your request.
Please try with below code may it help you:
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
HttpEntity requestEntity;
if (entity instanceof String) {
requestEntity = new HttpEntity(headers);
} else {
requestEntity = new HttpEntity(searchRequestInput, headers);
}
try {
String response = restTemplate.exchange(baseURL, HttpMethod.POST, requestEntity, String.class).getBody().toString());
} catch (HttpServerErrorException | HttpClientErrorException e) {
e.printStackTrace();
}
Try these utility methods:
public static <T> ResponseEntity<T> makeRestRequest(Object entity, String restUrl, HttpMethod method, Class<T> entityClass) {
RestTemplate restTemplate = new RestTemplate();
HttpEntity httpEntity = makeHttpEntity(entity);
ResponseEntity<T> response = null;
try {
response = restTemplate.exchange(restUrl, method, httpEntity, entityClass);
} catch (HttpClientErrorException e) {
e.printStackTrace();
return new ResponseEntity<>(e.getStatusCode());
}
return response;
}
public static <T> HttpEntity makeHttpEntity(T entity) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
HttpEntity<T> httpEntity = new HttpEntity<>(entity, headers);
return httpEntity;
}
Where in the these two method
entity: your input object
restUrl: your url
HttpMethod: POST/GET
entityClass: expected output object from the server
I am using spring RestTemplate for hitting a http service which works fine from a browser based HTTP client. But in my Java code, RestTemplate returns null. It doesn't hit the service (I tested using a debug point).
Below is my rest client code:
String url = "http://localhost:8080/webappn/app/decryptPassword";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_PLAIN);
HttpEntity<String> request = new HttpEntity<String>(getConnectionPassword().toString(), headers);
RestTemplate restTemplate = new RestTemplate();
try{
String response = restTemplate.postForObject(url, request, String.class);
this.password = response;
}catch(HttpStatusCodeException e){
utils.writeLoggerError(LOGGER, "Password could not be decrypted");
throw new Exception("Password could not be decrypted");
}
response is coming null when this code executes.
This is the rest service code:
#RequestMapping(value="/decryptPassword", method = RequestMethod.POST, produces = "text/plain")
public String decryptPassword(#RequestBody String cipherText){
try{
return myService.decryptPassword(cipherText);
} catch (Exception e){
throw new InternalServerErrorException("Password cannot be decrypted");
}
}
I figured out why this was happening. I have an authentication layer set up which filters all the requests. I added the JSESSIONID cookie in my request and it worked fine.
Although, it should have thrown an exception.