I created a generic method to call an external API (post call). Everything is working fine if external rest API returns 2** but, in case of any error an exception is raised.
Isn't is possible to treat this 4** response like a normal answer instead of generate an exception? The problem is that I need to get the error message (response body) and send back to the caller.
This is my code:
public ResponseEntity<String> post(String api, Map<String, Object> data) {
ResponseEntity<String> response = new ResponseEntity<String>(HttpStatus.OK);
try {
StopWatch stopwatch = new StopWatch();
stopwatch.start();
log.debug("post(): " + host + "/" + api );
// Set the headers
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// Convert the Map to Gson
Gson gson = new Gson();
String json = gson.toJson(data);
// Call the API
HttpEntity<?> request = new HttpEntity<>(json, headers);
response = new RestTemplate().postForEntity(host + "/" + api, request, String.class);
stopwatch.split();
log.info("All request completed [" + response.getStatusCode() + "] in " + stopwatch.getSplitTime());
return response;
} catch (Exception ex) {
log.error(ex);
throw new RuntimeException();
}
}
The external API, in case of error, returns this:
ResponseEntity<Integer> response = new ResponseEntity<Integer>();
[.. business code ..]
res.setResponse(new ResponseEntity<String>(CommonValue.userNotFound, HttpStatus.UNAUTHORIZED));
Use RestTemplate#exchange(..) methods that return a ResponseEntity. This will not throw an exception but will parse the answer into the ResponseEntity where you can retrieve the status code etc.
Related
I have a REST API to call and I have written the client using rest template. When executing, I am getting 400 status code. The same REST API is working fine when using POSTMAN. Below are the code snippets for API and caller. Do let me know if anyone catches anything.
REST API for POST method-
#ApiOperation(value = "Download repository as zip")
#ApiResponses({#ApiResponse(code = 200, message = ""), #ApiResponse(code = 400, message = "")})
#PostMapping(value = "/download", produces = {MediaType.APPLICATION_OCTET_STREAM_VALUE})
public ResponseEntity<StreamingResponseBody> downloadRepository(
#RequestBody #Validated final RepositoriesRequest repositoriesRequest) {
final Situation situation = this.situationsService.getSituationId(repositoriesRequest);
if (isNull(situation)) {
return ResponseEntity.notFound().build();
} else {
final ExtractionRequest extractionRequest = new ExtractionRequest(repositoriesRequest.getType(), situation,
repositoriesRequest.getDatabase());
if (!this.validateRequest(extractionRequest)) {
return ResponseEntity.badRequest().build();
}
final ExtractionResponse response = this.extractService.extractRepository(extractionRequest);
if (null == response) {
return ResponseEntity.notFound().build();
}
final InputStream inputStream = this.extractService.getFileFromS3(response.getRepositoryPath());
if (null == inputStream) {
return ResponseEntity.noContent().build();
}
final StreamingResponseBody bodyWriter = this.bodyWriter(inputStream);
return ResponseEntity.ok()
.header("Content-Type", "application/zip")
.header(CONTENT_DISPOSITION, "attachment; filename=\"repository-" + situation.getId() + ".zip\"")
.body(bodyWriter);
}
}
REST CLIENT using Rest Template with auth token and request body as input -
HttpEntity<MultiValueMap<String, Object>> buildLoadRepoRequest(
final SimulationContext context,
final List<String> tablesName,
final String simulationId,
final Integer offset) {
final Token token = this.authenticateOkoye(simulationId, offset);
LOGGER.info("Token Run: {}", token.getAccessToken());
final String database = this.getDatabaseForEnvironment();
final HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(APPLICATION_JSON_UTF8);
httpHeaders.set(AUTHORIZATION, "Bearer " + token.getAccessToken());
final MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("database", database);
body.add("monthlyClosingMonth", context.getMonthlyClosingDate());
body.add("repositorySnapshot", context.getRepository());
body.add("situationId", context.getSituationId());
body.add("tableNames", tablesName);
body.add("type", context.getRunType());
return new HttpEntity<>(body, httpHeaders);
}
Exception Handler -
#Override
#ExceptionHandler(HttpClientErrorException.class)
public void loadRepository(
final SimulationContext context,
final List<String> tablesName,
final String simulationId,
final Integer offset,
final Path repositoryPath) throws IOException {
LOGGER.info("[{}] [{}] repository tablesName: {}", simulationId, offset, tablesName);
this.restTemplate.setRequestFactory(this.getClientHttpRequestFactory());
final ClientHttpResponse response = this.restTemplate.postForObject(
this.repositoriesUrl,
this.buildLoadRepoRequest(context, tablesName, simulationId, offset),
ClientHttpResponse.class);
if (response != null && HttpStatus.OK == response.getStatusCode()) {
LOGGER.info(
"response status on simulation : {} - Context: {} - status: {}",
simulationId,
offset,
response.getStatusCode());
//this.helper.copy(response.getBody(), repositoryPath);
} else if (response != null && HttpStatus.NO_CONTENT != response.getStatusCode()) {
throw new JarvisException(
"Can't retrieve RWA repository on simulation " + simulationId + " Context:" + offset);
}
}
We have been looking into this issue since yesterday and still don't have any clue. So far we have tried postForEntity, exchange, changing the headers to proper setter methods and tried passing the parameters as an object also. None of them worked.
I have a strong feeling about something being wrong at header level while calling the API.
Did you try to use httpHeaders.setContentType(MediaType.APPLICATION_JSON)
Or add consumes to #PostMapping annotation with APPLICATION_JSON_UTF8 value
Running Spring 4.2.6.RELEASE, we are experiencing unexpected behavior with fairly typical RestTemplate usage; a 401 error is not being translated to a HttpClientErrorException.
Specifically, when we receive an expected 401 from the server, we receive a ResourceAccessException wrapping an IOException with the message Server returned HTTP response code: 401 for URL: ..., which is raised deep within the bowels of sun.net.www.protocol.http.HttpURLConnection.
Our template is configured more or less like so:
public RestTemplate restTemplate() {
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
// connections per route is not a meaningful limit for us, so set very high
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(10000);
poolingHttpClientConnectionManager.setMaxTotal(100);
CloseableHttpClient client = HttpClientBuilder.create().setConnectionManager(poolingHttpClientConnectionManager).build();
HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
Our usage of the client looks like:
protected <T, U> ResponseEntity<T> request(UserClientAccount account, U body, String url, HttpMethod httpMethod,
ParameterizedTypeReference<T> responseType, boolean retry) {
try {
HttpEntity<U> request = createHttpEntity(body, account.getToken(this));
return getRestTemplate().exchange(url, httpMethod, request, responseType, new HashMap<String, String>());
}
catch (HttpClientErrorException e) {
if (e.getStatusCode() == HttpStatus.UNAUTHORIZED) {
if (retry) {
log.warn("Unauthorized response for {}. Refreshing token to retry...", url);
refreshToken(account);
// tries again
return request(account, null, url, httpMethod, responseType, false);
}
else {
log.error("Unauthorized error calling {}. All attempts to retry exhausted ", url);
throw e;
}
}
throw new ProgramException("Error while performing " + httpMethod + " request to " + url + ". " +
"Response body: " + e.getResponseBodyAsString(), e);
}
}
Our catch of HttpClientErrorException is never hit; instead, we receive a ResourceAccessException with a cause of the above mentioned IOException.
What are we doing wrong?
I have to make registration using REST URL. REST services are written in Java now i have to pass the set of parameters in that secGameIds parameter is like this [100,102]. Example registration using Insomnia:::
{
"firstName":"parent111",
"lastName":"sadfsdf",
"email":"abc#bbc.com",
"date":"2000-06-09",
"phoneNum":"8765654454",
"gender":"male",
**"secGameIds":[0,0],**
"roleId":102
}
How should i provide secGameIds parameter value is it a ArrayList or Array?
for remaining values i have created JSONObject class object and adding values to that object and 'm appending that object to url
{
JSONObject json = new JSONObject();
json.put("fistName","aaa");
..
..
HttpPost post = new HttpPost(uri);
post.setHeader("Content-type", "application/json");
post.setEntity(new StringEntity(json.toString(), "UTF-8"));
DefaultHttpClient client = new DefaultHttpClient();
httpresponse = client.execute(post);
}
where as for secGameId i have tried like below,
{
int[] secGameId = {100,102};
}
-- gives me an error in back-end like "nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of int[] out of VALUE_NUMBER_INT token"
I even tried by using
{
ArrayList<Integer> secGameId = new ArrayList<String>();
secGameId.add(100);
secGameId.add(102);
}
and passing to value...
{
json.put("secGameIds":secGameId)
}
again at server side i kicked with the same error.
Can anyone help me?
public static String httpPost(HashMap<String, String> map, String url,String token) {
Log.e("call ", "running");
HttpRequest request;
if(token!=null){
request = HttpRequest.post(url).accept("application/json")
.header("Authorization", "Token " + AppInfo.token).form(map);
}
else
request = HttpRequest.post(url).accept("application/json").form(map);
int responseCode = request.code();
String text = request.body();
Log.e("response", " "+responseCode+ " "+ text);
if(responseCode==400){
return "invalid_tocken";
}
else if(responseCode<200 || responseCode>=300) {
return "error";
}
return text;
}
Hope you can convert the JSONArray to HashMap. If you instead need to post it as a JSONArray itself, then OkHttp library will help you.
I am trying to update or create xml file if not present. Then I use code below to send the file using PUT method of a service.
public void importClusterProperties(RestManPropertyHolder propertyHolder,File file,String id) throws RestManServiceException {
testRestTemplate = new TestRestTemplate(propertyHolder.getSbusUserName(), propertyHolder.getSbusUserPassword());
String sbusUrl = utils.prepareGatewayURI(propertyHolder);
try {
HttpHeaders requestHeaders = new HttpHeaders();
List <MediaType> mediaTypeList = new ArrayList<MediaType>();
mediaTypeList.add(MediaType.APPLICATION_ATOM_XML);
requestHeaders.setAccept(mediaTypeList);
requestHeaders.setContentType(MediaType.APPLICATION_ATOM_XML);
HttpEntity<String> requestEntity = new HttpEntity<String>(requestHeaders);
// Create the HTTP PUT request,
ResponseEntity<String> response = testRestTemplate.exchange(sbusUrl + "/clusterproperty?",HttpMethod.PUT, requestEntity,String.class);
if (null != response) {
System.out.println("RESPONSE::" + response.toString());
}
} catch (RestClientException rce) {
System.out.println("REST EXCEPTION:::" + rce.getMessage());
}
}
How to pass raw xml file into RestTemplate without converting it first into a java object?
enter image description here
Convert file to byte array and send it using ByteArrayHttpMessageConverter.
RestTemplate restTemplate = new RestTemplate();
// Add ByteArrayHttpMessageConverter if not present by default.
restTemplate.getMessageConverters().add(
new ByteArrayHttpMessageConverter());
String fileName = "path + file name";
// FileUtils is from Apache Commons IO
// import org.apache.commons.io.FileUtils;
byte[] requestBody = FileUtils.readFileToByteArray(new File(fileName));
HttpEntity<byte[]> requestEntity = new HttpEntity<byte[]>(requestBody , requestHeaders);
// Create the HTTP PUT request,
ResponseEntity<byte[]> response =
restTemplate.exchange("URL ...." , HttpMethod.PUT ,
requestEntity , byte[].class);
I struggled with an extrange spring behavior using RestTemplate (org.springframework.web.client.RestTemplate) without success.
I use in my hole application below code and always receive an XML response, which I parse and evaluate its result.
String apiResponse = getRestTemplate().postForObject(url, body, String.class);
But can't figure out why a server response is in JSON format after executing:
String apiResponse = getRestTemplate().getForObject(url, String.class);
I've debugged at low level RestTemplate and the content type is XML, but have no idea why the result is in JSON.
When I access from a browser the response is also in XML, but in apiResponse I got JSON.
I tried many options after reading Spring documentation
http://docs.spring.io/spring/docs/3.0.x/api/org/springframework/web/client/RestTemplate.html
Also tried to modify explicitly the headers but still can't figure it out.
I debugged RestTemplate class and noticed that this method is always setting application/json:
public void doWithRequest(ClientHttpRequest request) throws IOException {
if (responseType != null) {
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
for (HttpMessageConverter<?> messageConverter : getMessageConverters()) {
if (messageConverter.canRead(responseType, null)) {
List<MediaType> supportedMediaTypes = messageConverter.getSupportedMediaTypes();
for (MediaType supportedMediaType : supportedMediaTypes) {
if (supportedMediaType.getCharSet() != null) {
supportedMediaType =
new MediaType(supportedMediaType.getType(), supportedMediaType.getSubtype());
}
allSupportedMediaTypes.add(supportedMediaType);
}
}
}
if (!allSupportedMediaTypes.isEmpty()) {
MediaType.sortBySpecificity(allSupportedMediaTypes);
if (logger.isDebugEnabled()) {
logger.debug("Setting request Accept header to " + allSupportedMediaTypes);
}
request.getHeaders().setAccept(allSupportedMediaTypes);
}
}
}
Could you give an idea?
I could solve my issue with RC.'s help. I'll post the answer to help other people.
The problem was that Accept header is automatically set to APPLICATION/JSON so I had to change the way to invoke the service in order to provide the Accept header I want.
I changed this:
String response = getRestTemplate().getForObject(url, String.class);
To this in order to make the application work:
// Set XML content type explicitly to force response in XML (If not spring gets response in JSON)
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_XML));
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
ResponseEntity<String> response = getRestTemplate().exchange(url, HttpMethod.GET, entity, String.class);
String responseBody = response.getBody();