RestTemplate - handle potential NullPointerException when response body is null - spring

I'm writing client that calls some backend REST service. I'm sending Product object which will be saved in DB and returned in response body with generated productId.
public Long createProduct(Product product) {
RestTemplate restTemplate = new RestTemplate();
final String url = " ... ";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Product> productEntity = new HttpEntity<>(product, headers);
try {
ResponseEntity<Product> responseEntity = restTemplate.postForEntity(url, productEntity, Product.class);
Product product = responseEntity.getBody();
return product.getProductId();
} catch (HttpStatusCodeException e) {
logger.error("Create product failed: ", e);
throw new CustomException(e.getResponseBodyAsString(), e, e.getStatusCode().value());
}
This product.getProductId() looks like potential NullPointerException if product i.e. responseEntity.getBody() is null, should I handle it somehow?
I have looked examples over internet of using RestTemplate postFprEntity, getForEntity ... but didn't find any example that handle NPE. I suppose that if body of response cannot be set, it will be some exception thrown and status code 5xx.
Is it possible when response status code is 200, that body can be null?

Is it possible when response status code is 200, that body can be
null?
Yes, it is quite possible and totally depends on the server. Normally, some REST APIs and Spring REST Repositories will return 404 if resource is not found but better safe than sorry.
This product.getProductId() looks like potential NullPointerException
if product i.e. responseEntity.getBody() is null, should I handle it
somehow?
Of course you should.
You can check if responseEntity.hasBody() && responseEntity.getBody() != null. And from there either throw an Exception of your own or handle however you see fit.

Related

How to handle the http response from an api call efficiently using spring boot

When we fire an api call to a 3rd party service, we can get different HTTP responses (200, 404 etc.). How can we handle them in a standard way?
private ResponseEntity<ResultHolder> responseEntity;
public ResponseEntity<ResultHolder> serviceTest(String searchText, String countryCode) {
logger.info("Service started");
String url = prepareUrl(searchText,countryCode); //custom method
HttpHeaders header = new HttpHeaders();
prepareHeader(header); //custom method
HttpEntity<String> requestEntity = new HttpEntity<String>(header);
try {
logger.info("Calling the API");
responseEntity = restClient.exchange(url,
HttpMethod.GET,
requestEntity,
ResultHolder.class);
}catch (Exception e) {
logger.error("Exception while calling the API "+ e);
//Here I am trying to get the value of response code and handle based on that
//Is this the right way to solve the problem?
if(responseEntity.getStatusCodeValue() != 200) {
responseEntity = new ResponseEntity<ResultHolder>(
HttpStatus.BAD_REQUEST);
}
}
logger.info("Service Ended");
return responseEntity;
}
What if I want to display distinct custom messages for server side errors and for user errors like 'No Internet Connection'.
Kindly help me to understand the good practises in this area.
Thank you

How to get custom messages when you use response.getStatusText()

HttpClientErrorException always produces the following result for me:
HttpClientErrorException: 400 null
... and the null part is what worries me. Shouldn't this be the place where the message of the server-side exception is supposed to be?
I checked the source code of the HTTP client to see where the client-side exception is thrown. It looks like this:
throw new HttpClientErrorException(statusCode, response.getStatusText(), response.getHeaders(), getResponseBody(response), getCharset(response));
Debugging this call revealed that response.getStatusText() is null in my case.
My question is: How do you design your ResponseEntity on the server-side such that the HTTP client finds the server-side exception message in response.getStatusText() instead of null?
Here is my Exception
#ExceptionHandler({ MyCustomException.class })
public ResponseEntity<String> handleException(final HttpServletRequest
req, final MyCustomException e) {
HttpHeaders headers = new HttpHeaders();
headers.set("Content-type", "text/plain");
String body = e.toString();
return new ResponseEntity<>(body, headers, HttpStatus.BAD_REQUEST);
}

java.lang.IllegalArgumentException: Comparison method violates its general contract! in Spring Rest Template

I am facing a weird issue while calling a REST url using Spring's RestTemplate.I am using Spring Boot.
The error is occurring only after approx 10 to 15 successful calls and thereafter erratically. I can smoothly exchange data before the error, in the first 1 to 15 calls approx. Url is like someresturl/param1/param2/param3.
public ResponseEntity<String> callRestUrl(CustomReqClass req) {
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
StringBuilder url = new StringBuilder("someresturl");
finishTaskUrl.append("/").append(param1).append("/").append(param2).append("/").append(param3);
ResponseEntity<String> response = null;
HttpEntity<CustomReqClass> request = new HttpEntity<CustomReqClass>(req, getHTTPHeaders());
try {
//first approach
response = restTemplate.postForEntity(url.toString(), request, String.class, Collections.<String, String>emptyMap());
//second approach
response = restTemplate.exchange(url.toString(), HttpMethod.POST, request, String.class);
} catch (Exception e) {
LOGGER.info("Error calling url" + e);
}
return response;
}
public MultiValueMap<String, String> getHTTPHeaders() {
MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>();
headers.add("Authorization", "Basic authabcdxyz");
headers.add("Content-Type", "application/json");
return headers;
}
Here I am autowiring restTemplate object in the class where I am using this.
I have tried both the above methods postForEntity and exchange of Rest template. Error is occurring for both.
The exception I am getting after first few successful attempts:
java.lang.IllegalArgumentException: Comparison method violates its general contract!
As an additional thought, the above piece of code is being scheduled by Spring Scheduler mechanism. Is it possible internal threading used in scheduler is causing this issue?

Get status response (like 400,500)

I am using the spring Framework which has the header below:
import org.springframework.web.client.RestTemplate;
I want to fetch the status code to write my Logger. How do I get the response from restTemplate?
public boolean performTransition(String transitionId,String jiraId){
JiraID id = new JiraID(transitionId);
JiraTransition transition = new JiraTransition();
transition.setTransition(id);
String transitionUrlFormat = String.format(transitionUrl,jiraId);
RestTemplate template = new RestTemplate();
HttpEntity epicEntityRequest = new HttpEntity(transition,createHttpHeaders());
HttpEntity<String> epicEntityResponse= template.exchange(transitionUrlFormat , HttpMethod.POST, epicEntityRequest, String.class);
//TODO: verify code 204
ResponseEntity<String> responseEntity= (ResponseEntity<String>) epicEntityResponse;
epicEntityResponse.getBody();
//System.out.println("LOG" +responseEntity);
//responseEntity.getStatusCode();
HttpStatus statusCode = responseEntity.getStatusCode();
return true;
}
Also, I want to check for the response code above 400 I want write log.warning().
Question needs more elaboration. Are you meaning something like this:
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, request, String.class);
int statusCode = response.getStatusCode().value();
This gives status code as an int, you can do something like:
if(statusCode > 400){
//Log here
}
The class ResponseEntity can give you the entire HTTP response status code, body and headers.
Ofcourse, you need to initialize restTemplate, either using default:
RestTemplate restTemplate = new RestTemplate();
This uses, default: SimpleClientHttpRequestFactory, or if you want something more configurable you can use: HttpComponentsClientHttpRequestFactory which has many configs, like connection pooling etc, read timeout, connection timeout etc.

RestTemplate gives 400 Bad Request Error on a Get Request

When I try to make a get request with Spring's RestTemplate, it gives 400 BAD Request. I can call the same url from javascript successfully with the headers below :
But the code below does not work. What might be the cause?
public Entity getEntityByUri(String uri) {
String req = "http://live.dbpedia.org/sparql?query=DESCRIBE%20%3Chttp://dbpedia.org/resource/Concept_learning%3E&format=application%2Fjson-ld";
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.ALL));
HttpEntity<String> httpEntity = new HttpEntity<String>(headers);
new RestTemplate().exchange(req, HttpMethod.GET, httpEntity, Map.class);
Entity entity = new Entity();
return entity;
}
Your url is already encoded. Popular browsers such as Chrome are capable of understanding and responding appropriately. However, it's not the same case with RestTemplate.
I had to decode your uri here and the decoded uri is DESCRIBE <http://dbpedia.org/resource/Concept_learning>
Having checked the browser console, I got to know you have two query strings passed in the url, they are query and format holding values DESCRIBE <http://dbpedia.org/resource/Concept_learning> and application/json-ld respectively.
I assume Entity class is the pojo class of json response.
Have created Entity as from your json response:
public class Entity {
private String value;
private String type;
// getters and setters omitted for brevity
}
Finally in your getEntityByUri method have got the instance of UriComponentsBuilder which handles uri encoding and query params.
To sum up, your getEntityByUri looks below.
public HttpEntity<Entity> getEntityByUri() {
String req = "http://live.dbpedia.org/sparql";
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(req)
.queryParam("query",
"DESCRIBE <http://dbpedia.org/resource/Concept_learning>")
.queryParam("format", "application/json-ld");
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.ALL));
HttpEntity<String> httpEntity = new HttpEntity<String>(headers);
return new RestTemplate().exchange(builder.build().encode().toUri(), HttpMethod.GET, httpEntity, Entity.class);
}
The above method didn't throw HTTP400 as the required query params have been passed in builder object.
Hope this helps and good luck!
Anyone getting same error make sure your URL is decoded means no percent symbols in url (if space in param values).
This worked for me
try {
requestURL = URLDecoder.decode("http://api.com?p=1&groups=3212&affected-since=2019-06-06T14%3A11%3A14.880&detail=full&after-id=43536", "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
Maybe
headers.setAccept(Arrays.asList(MediaType.ALL));
generates a malformed "Accept" header field? (FWIW, why do you send it at all???)

Resources