How to capture error responses from Rest Template in spring boot? - java

I've 2 springboot REST APIs REST-A & REST-B. REST-B is interacting with mongodb for CRUD operations. And REST-A is calling REST-B endpoints for different reasons.
Controller in REST-B (Customer API)
public class CustomerController {
#Autowired
private CustomerRepository customerRepository;
#GetMapping(value = "/customers/{id}")
public ResponseEntity<Customer> getCustomerByExternalReferenceId(#PathVariable(value = "id") String id)
throws ResourceNotFoundException {
System.out.println("Customer id received :: " + id);
Customer customer = customerRepository.findByExternalCustomerReferenceId(id)
.orElseThrow(() -> new ResourceNotFoundException("Customer not found for this id :: " + id));
return ResponseEntity.ok().body(customer);
}
}
This endpoint works fine if I call from postman for both if customer found in DB and if customer not found in DB.
Now, if I try to call the same endpoint from REST-A and if customer found in DB I can get the response.
String url = "http://localhost:8086/customer-api/customers/{id}";
String extCustRefId =
setupRequest.getPayload().getCustomer().getCustomerReferenceId();
// URI (URL) parameters
Map<String, String> urlParams = new HashMap<>();
urlParams.put("id", extCustRefId); // here I tried with id that exists in DB and getting 200 ok response
HttpHeaders headers = new HttpHeaders();
headers.set("X-GP-Request-Id", "abc-xyz-123");
headers.set("Content-Type", "application/json");
headers.set("Accept", "application/json");
headers.set("Content-Length", "65");
String searchurl = UriComponentsBuilder.fromUriString(url).buildAndExpand(urlParams).toString();
System.out.println(searchurl);
HttpEntity request = new HttpEntity(headers);
RestTemplate restTemplate = new RestTemplate();
try {
ResponseEntity<String> response = restTemplate.exchange(
searchurl,
HttpMethod.GET,
request,
String.class
);
} catch (Exception e) {
e.printStackTrace();
}
But if there's no customer found from REST-B (Customer API) then I'm getting
http://localhost:8086/customer-api/customers/customer-528f2331-d0c8-46f6-88c2-7445ee6f4821
Customer id received :: customer-528f2331-d0c8-46f6-88c2-7445ee6f4821
org.springframework.web.client.HttpClientErrorException: 404 null
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:78)
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:613)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:531)
How do I call rest endpoint from one springboot application to another and handle response properly?

You can get the response body from HttpClientErrorException as follows:
try {
ResponseEntity<String> response = restTemplate.exchange(
searchurl,
HttpMethod.GET,
request,
String.class
);
} catch (HttpClientErrorException e) {
String errorResponseBody = e.getResponseBodyAsString();
e.printStackTrace();
}
You can then use Jackson ObjectMapper to map the String to a Java object.

Related

How to use CloudHealth API (provided by vmware) for fetching reports of client or tenant in spring boot application?

I want to implement CloudHealth API in my Spring Boot application. I want to fetch report of particular client. I have a dropdown where logged in user select reports and that report will be directly fetched from CloudHealth platform. I want to do that thing in my application. I want to generate JSON response of custom report. I followed API documentation available at https://apidocs.cloudhealthtech.com/#reporting_data-for-custom-report
but I am getting 404 Not Found: "{"error":"Record with id not found."}"
This is the code written in my service class:
public String getCustomReportData(String reportId) {
ResponseEntity<String> responseEntity = null;
String response = null;
try {
final String uri = "https://chapi.cloudhealthtech.com/olap_reports/custom/"+reportId;
RestTemplate restTemplate = new RestTemplate();
HttpHeaders header = new HttpHeaders();
header.set(HttpHeaders.AUTHORIZATION, "Bearer my-api-key");
header.set(HttpHeaders.ACCEPT,"application/json");
HttpEntity<String> requestEntity = new HttpEntity<String>("body",header);
responseEntity = restTemplate.exchange(uri, HttpMethod.GET, requestEntity, String.class);
response = responseEntity.getBody();
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
return response;
}
This is main endpoint in my restcontoller:
#RequestMapping(value = {"/custom_report/{report_id}"}, method = {RequestMethod.GET, RequestMethod.POST}, produces = {MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<Object> getCustomCloudHealthReports(HttpServletRequest request,#PathVariable("report_id") String reportId){
try {
String response = standardReportService.getCustomReportData(reportId);
return new ResponseEntity<Object>(response, HttpStatus.OK);
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
return new ResponseEntity<Object>("Please try again later", HttpStatus.INTERNAL_SERVER_ERROR);
}
}

Cannot send POST request with RestTemplate

I have 2 services:
Service Web on http://localhost:8080 and Service Engine on http://localhost:8081
Service Web sends a POST request to Service Engine through this code :
String checkWinUrl = customProperties.getEngineUrl() + "/checkWin";
RestTemplate restTemplate = new RestTemplate();
HttpEntity<GameDto> request = new HttpEntity<>(new GameDto(game));
try {
ResponseEntity<CheckWinResult> response = restTemplate.exchange(
checkWinUrl,
HttpMethod.POST,
request,
CheckWinResult.class);
CheckWinResult checkWinResult = response.getBody();
if (checkWinResult != null && checkWinResult.isWin()) {
Set<Move> result = new HashSet<>();
for (MoveDto move : checkWinResult.getWinMoves()) {
result.add(Move.builder().color(GomokuColor.GREEN).columnIndex(move.getColumnIndex()).rowIndex(move.getRowIndex()).build());
}
return result;
}
} catch (RestClientException e) {
log.error("Error while computing checkWin : " + e.getMessage());
}
RestController
#PostMapping("/checkWin")
public CheckWinResult checkWin(#RequestBody GameDto game) {
return engineService.checkWin(game);
}
and it works fine, Engine Service receives the request properly.
But when Engine Service sends a request to Web Service :
String webAppUrl = customProperties.getWebAppUrl() + "/engineMessage";
RestTemplate restTemplate = new RestTemplate();
HttpEntity<String> request = new HttpEntity<>(message.toString());
restTemplate.exchange(webAppUrl, HttpMethod.POST, request, Void.class);
RestController
#PostMapping("/engineMessage")
public void engineMessage(#RequestBody String engineMessage) {
JSONObject jsonMessage = new JSONObject(engineMessage);
WebSocketMessage webSocketMessage = new WebSocketMessage();
webSocketMessage.setType(MessageType.valueOf(jsonMessage.getString("type")));
webSocketMessage.setContent(jsonMessage.getString("content"));
webSocketController.sendMessage(webSocketMessage);
}
Web Service just never receives the request.
Any ideas?
Thank you.

Passing authorities and user principal from rest client to server spring boot

I have to call one secured endpoint from rest client and at the controller side it require the authorities and user principal information to be sent from client.
String endpoint="http://localhost:8096/polygons/34";
// endpoint="https://dop-int.edosdp.ericsson.se/polygon-manager/polygons/34";
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth("mahi", "ChangeM6");
headers.setConnection("keep-alive");
HttpEntity<String> httpEntity = new HttpEntity<String>(headers);
ResponseEntity<Long> exchange = restTemplate.exchange(endpoint,HttpMethod.GET, httpEntity, Long.class);
how can send at least one role(ADMIN or GUEST_USER) information from client .
IS there any way I can wrap up all user info in a dummy session and send it to the serer.
Thanks ,
Mahi
No! It's a bad idea for the client to modify any kind of session information including cookies. Only the server should be allowed to do that.
Since your requirement is to check for user role on a specific url, you can set a custom request header and check for it within the controller method itself:
Example code:
#GetMapping("/polygons")
public String getPolygons(HttpServletRequest request) {
String userRole = request.getHeader("user-role");
if(userRole != null && userRole.toLowerCase().equals("admin")) {
System.out.print("Role provided: " + userRole);
// ...
return "some-data";
}
System.out.print("Role not provided!");
return "error";
}
You could also set the user role in the request body for a post request.
public class RequestParams {
private String userRole;
// ...
}
#PostMapping("/polygons")
public String getPolygons(#RequestBody RequestParams requestParams) {
String userRole = requestParams.getUserRole();
if(userRole != null && userRole.toLowerCase().equals("admin")) {
System.out.print("Role provided: " + userRole);
// ...
return "some-data";
}
System.out.print("Role not provided!");
return "error";
}
If your requirement is to check for the user role on multiple urls then you should consider writing a servlet filter.
EDIT:
I think I too faced a similar situation in the past. I ended up using apache's httpclient library instead of resttemplate.
Here's some sample code:
private List<OrganizationDTO> getUserOrgUnits(String loggedInUserId, String token) {
List<OrganizationDTO> userList = new ArrayList<OrganizationDTO>();
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(getUserOrgUnitsApiURL());
try {
// Setting header
httpGet.setHeader("Authorization", "Bearer " + token);
httpGet.setHeader("Content-type", "application/json");
// Setting custom header
httpGet.setHeader(USERID_HEADER_NAME, loggedInUserId);
CloseableHttpResponse response = httpClient.execute(httpGet);
String result = EntityUtils.toString(response.getEntity());
JsonNode node = null;
ObjectMapper mapper = new ObjectMapper();
node = mapper.readTree(result);
Iterable<JsonNode> list = node.path("data");
for (JsonNode jsonNode : list) {
OrganizationDTO dto = mapper.treeToValue(jsonNode, OrganizationDTO.class);
userList.add(dto);
}
} catch (Exception e) {
log.error("getUserOrgUnits: Exception.");
e.printStackTrace();
}
return userList;
}

GET Request Works in Postman but not with SpringBoot RestTemplate

I have a two Spring Boot application. One is a rest client that makes rest calls. Another app that has only Rest endpoint.
When the Rest client hits the rest endpoint, it fails.
This is the code used to hit the rest endpoint:
HttpHeaders headers = new HttpHeaders();
headers.set(ACCEPT, APPLICATION_JSON);
headers.set(CONTENT_TYPE, APPLICATION_JSON);
HttpEntity entity = new HttpEntity(headers);
UriComponentsBuilder builder = UriComponentsBuilder
.fromHttpUrl(url)
.queryParam(EMAIL, URLEncoder.encode(email, "UTF-8"))
.queryParam(ADDRESS, URLEncoder.encode(address, "UTF-8"));
ResponseEntity<Address> response =
commonRestTemplate
.exchange(builder.toUriString(), 
HttpMethod.GET, entity, Address.class);
This is the rest endpoint the client is trying to hit:
#RestController
#AllArgsConstructor
public class AddressController {
private final RestTemplate commonRestTemplate;
// constructor and other rest endpoints
#RequestMapping(value = "/", method = RequestMethod.GET)
public #ResponseBody ResponseEntity<Address> getAddress(#RequestParam String email, #RequestParam String address) {
try {
// do soemthing
} catch (RuntimeException e)
{
LOGGER.error(e.getMessage(), e);
return status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
}
This is the error I'm seeing in the app with the rest endpoint:
2020-03-26 16:33:53.619 WARN 9 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'address' is not present]
2020-03-26 16:50:02.691 ERROR 9 --- [nio-8080-exec-9] u.c.h.s.s.controller.AddressController : Key may not be empty
Why does the Rest call work with Postman but not my rest client?
I've also tried with and without encoding the special characters in the rest client with no luck. I can't seem to see what I am missing
Try below changes
UriComponentsBuilder builder = UriComponentsBuilder
.fromHttpUrl(url)
.queryParam("email", URLEncoder.encode(email, "UTF-8"))
.queryParam("address", URLEncoder.encode(address, "UTF-8"));
#RestController
#AllArgsConstructor
public class AddressController {
private final RestTemplate commonRestTemplate;
// constructor and other rest endpoints
#RequestMapping(value = "/", method = RequestMethod.GET)
public #ResponseBody ResponseEntity<Address> getAddress(#RequestParam("email") String email, #RequestParam("address") String address) {
try {
// do soemthing
} catch (RuntimeException e)
{
LOGGER.error(e.getMessage(), e);
return status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
}
I had this problem too. It was solved when I used uri instead string in exchange method.
ResponseEntity<String> responseEntity = null;
Map<String, String> map = generate map to keep key and value of necessaryparameters;
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl("SERVICE_URL");
map.forEach((k, v) -> {
uriComponentsBuilder.queryParam(k, v);
});
URI uri = uriComponentsBuilder.build(false).encode("windows-1256").toUri();
responseEntity = new RestTemplate().exchange(uri, HttpMethod.POST, request, String.class);
can be 2 issues:
static ADDRESS is properly defined and referring to "address".
another one, address value is not null. print address value before calling restTemplate.

Rest Api call gives error 400 using Spring Oauth2

I'm building a rest API using Spring security Oauth2 to secure it.
The following curl command runs succesfully and I get the token:
curl -X POST -vu clientapp:123456 http://localhost:8080/dms-application-0.0.1-SNAPSHOT/oauth/token -H "Accept: application/json" -d "password=spring&username=roy&grant_type=password&scope=read%20write&client_secret=123456&client_id=clientapp"
The following test to get the token also runs succesfully:
#Test
public void getAccessToken() throws Exception {
String authorization = "Basic " + new String(Base64Utils.encode("clientapp:123456".getBytes()));
String contentType = MediaType.APPLICATION_JSON + ";charset=UTF-8";
// #formatter:off
String content = mvc
.perform(
post("/oauth/token")
.header("Authorization", authorization)
.contentType(
MediaType.APPLICATION_FORM_URLENCODED)
.param("username", "roy")
.param("password", "spring")
.param("grant_type", "password")
.param("scope", "read write")
.param("client_id", "clientapp")
.param("client_secret", "123456"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.access_token", is(notNullValue())))
.andExpect(jsonPath("$.token_type", is(equalTo("bearer"))))
.andExpect(jsonPath("$.refresh_token", is(notNullValue())))
.andExpect(jsonPath("$.expires_in", is(greaterThan(4000))))
.andExpect(jsonPath("$.scope", is(equalTo("read write"))))
.andReturn().getResponse().getContentAsString();
// #formatter:on
String token= content.substring(17, 53);
}
However, when calling the rest end point externally from a webapp using Spring RestTemplate gives me a http error 400.
Below the code:
#RequestMapping(value = "/authentication", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity authenticate(#RequestBody CredentialsDto credentials) {
try {
String email = credentials.getEmail();
String password = credentials.getPassword();
String tokenUrl = "http://" + env.getProperty("server.host") + ":8080" + "/dms-application-0.0.1-SNAPSHOT" + "/oauth/token";
// create request body
JSONObject request = new JSONObject();
request.put("username", "roy");
request.put("password", "spring");
request.put("grant_type","password");
request.put("scope","read write");
request.put("client_secret","123456");
request.put("client_id","clientapp");
// set headers
HttpHeaders headers = new HttpHeaders();
String authorization = "Basic " + new String(Base64Utils.encode("clientapp:123456".getBytes()));
String contentType = MediaType.APPLICATION_FORM_URLENCODED.toString();
headers.set("Authorization",authorization);
headers.set("Accept","application/json");
headers.set("Content-Type",contentType);
HttpEntity<String> entity = new HttpEntity<String>(request.toString(), headers);
// send request and parse result
ResponseEntity<String> loginResponse = restClient.exchange(tokenUrl, HttpMethod.POST, entity, String.class);
// restClient.postForEntity(tokenUrl,entity,String.class,)
if (loginResponse.getStatusCode() == HttpStatus.OK) {
//JSONObject userJson = new JSONObject(loginResponse.getBody());
String response = loginResponse.getBody();
return ResponseEntity.ok(response);
} else if (loginResponse.getStatusCode() == HttpStatus.UNAUTHORIZED) {
// nono... bad credentials
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
}
return null;
}
And the error I get:
"Missing grant type"
Any ideas of what can be wrong or any other ways to do it? Because I'm completely stuck on this.
Thank you
Try to do it like this:
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("username", "roy");
map.add("password", "spring");
map.add("grant_type", "password");
map.add("scope", "read write");
map.add("client_secret","123456");
map.add("client_id","clientapp");
HttpEntity request = new HttpEntity(map, headers);
One more thing, when you ask for a token make sure not to send a json request, but with this header:
headers.add("Content-Type", "application/x-www-form-urlencoded");

Categories