I am calling a REST API and capturing response code and message in a table in case of any exception occurs.
catch (HttpClientErrorException ex) {
logger.error("HttpClientErrorException occures in calling API");
System.out.println(ex.getStatusCode());
System.out.println(ex.getMessage());
Output:
HttpClientErrorException occures in calling API
400 BAD_REQUEST
400 : [{"errors":[{"status":"400","code":"1002","title":"Missing parameter","detail":"Id element is mandatory."}]}]
It seems I can get the required value StatusCode and Message from ex.getMessage() but how can I extract these values?
You see an answer from server being called in message property, so format depends on server/endpoint you are calling.
In this particular case you can parse JSON and work with it like an object and access all props. Simpler (but uglier) way is to extract all you need by regex.
But again, you will not have universal solution, since it depends on the servers answer.
Parsing as JSON with Jackson library example:
public class ErrorMessage { // getter/setter are omitted
String title;
String detail;
String code;
String status;
}
catch (HttpClientErrorException ex) {
logger.error("HttpClientErrorException occures in calling API");
ObjectMapper objectMapper = new ObjectMapper();
ErrorMessage errorMessage = objectMapper.convertValue(ex.getMessage(), ErrorMessage.class);
System.out.println(errorMessage.getTitle); // <- you have an object now, so you can do whatever you want
}
Related
#PostMapping()
public ResponseEntity<?> getCall(#Valid #RequestBody Request request) {
String requestJson = null;
try {
requestJson = ObjectMapperUtil.writeValueAsString(request);
log.info(requestJson) // will this introduce latency in my api.
return ResponseEntity.ok(service.getData(request));
} catch (Exception e) {
log.error(requestJson);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Request.builder().errors(INTERNAL_SERVER_ERROR)).build());
}
}
Just want to know that if we print the request body in json format after converting using ObjectMapper, what will be the impact on the latency on the api. Should we go ahead with #toString logging only. What's the good trade-off here.
If you're worried about latency, add an if statement around that code (most logging frameworks have such check methods):
String requestJson = null;
try {
if (log.isInfoEnabled()) {
requestJson = ObjectMapperUtil.writeValueAsString(request);
log.info(requestJson);
}
return ResponseEntity.ok(service.getData(request));
} catch (Exception e) {
if (requestJson != null) {
log.error(requestJson, e);
} else {
log.error("Failed to convert '{}' to JSON", request, e);
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Request.builder().errors(INTERNAL_SERVER_ERROR)).build());
}
Note that if the conversion of the object to JSON fails, requestJson will remain null, and there's no point in logging it. I also didn't add a check for log.isErrorEnabled() because a) that's almost always the case, and b) there's no logic involved in the error logging; any filtering will be done by the logger itself. Also note that I included the exception in the logging as well - you really want to know why the failure occurred.
There will still be latency, but only if needed. You can also consider moving the conversion into the catch (which needs its own try-catch). That way, the request JSON will only be logged if there's an error.
In my experience, limited though it may be, I have only ever seen the ExceptionHandler class used to immediately return exceptions. I know this is the purpose of an ExceptionHandler class but this brings me to my question: If a request fails validation, is it possible for the ExceptionHandler class to "fix" the request body and re-run the request?
As an example, given the following object:
public class Person {
#Pattern(regexp = "[A-Za-z]")
private String firstName;
}
Could the following Handler class:
#ExceptionHandler(ParameterNotValidException.class)
public Map<String, String> handleValidationExceptions(
ParameterNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return errors;
}
Be modified like this:
#ExceptionHandler(ParameterNotValidException.class)
public void handleValidationExceptions(String requestBody) {
requestBody = removeSpecialCharacters(requestBody);
try {
personController.putPerson(requestBody);
} catch (Exception e) {
//fail gracefully
}
}
My apologies in advance, this is my first question on StackOverflow.
It is not acceptable. ExceptionHandler is a common place where we can manage and handle exceptions and send the respective error code for the API response.
See documentation.
It is designed for:
Handle exceptions without the #ResponseStatus annotation (typically predefined exceptions that you didn’t write)
Redirect the user to a dedicated error view
Build a totally custom error response
In your case special characters should be handled at json serialisation\deserialisation stage. Escape JSON string in Java
I have below code use to POST JSON object to the following URL
HttpEntity messageEntity = new HttpEntity(message, buildHttpHeaders(getTerminalId()));
String theUrl = "http://123.433.234.12/receive";
try {
System.out.println("In try block");
ResponseEntity<Dto> responseEntity= restTemplate.exchange(theUrl, HttpMethod.POST, messageEntity, Dto.class);
} catch (HttpStatusCodeException ex) {
// get http status code
}
If the URL is invalid or service unavailable, I want it throw error status code like 404 or 503. Unfortunatelly it will always stop at the try block..Is there a way to solve that ?
Output
In try block
Edit
String theUrl = "http://123.433.234.12/receive" + transactionId; //invalid Id
try {
System.out.println("=========start=========");
ResponseEntity<Dto> responseEntity= restTemplate.exchange(theUrl, HttpMethod.POST, messageEntity, Dto.class);
System.out.println("=========end=========");
} catch (HttpStatusCodeException ex) {
String a = ex.getStatusCode().toString();
System.out.println(a);
}
Output
=========start=========
2017-09-22 14:54:54 [xles-server-ThreadPool.PooledThread-0-running] ERROR c.r.abc.jpos.JposRequestListener - Error HttpStatusCode 500org.springframework.web.client.HttpServerErrorException: 500 null
It stop and not display ========end ======== or any status code in catch block
Valid url
http://abc0/receive/hello
If I change to
http://abc0/recei/hello
I will get 404 in catch block, it look fine. But when I change to another url that not exits,example
http://123.433.234.12/receive
it is in try block .Why ????
With reference to this doc, you should catch RestClientException instead of just HttpStatusCodeException.
If you want to throw exception in specific scenarios you can do it like this
try {
restTemplate.exchange(...);
}
catch (RestClientException e) {
// implies error is related to i/o.
if (e instanceof ResourceAccessException) {
// java.net.ConnectException will be wrapped in e with message "Connection timed out".
if (e.contains(ConnectException.class)) {
// handle connection timeout excp
}
} else if (e instanceof HttpClientErrorException) {
// Handle all HTTP 4xx error codes here;
} else if (e instanceof HttpServerErrorException) {
// Handle all HTTP 5xx error codes here
}
}
for HttpClientErrorException you can get error code from excption as shown below
HttpClientErrorException clientExcp = (HttpClientErrorException) e;
HttpStatus statusCode = clientExcp.getStatusCode();
like wise, you could get error code for HttpServerErrorException.
As far as I remember RestTemplate.exchange method throws RestClientException. You have HttpStatusCodeException in your catch clause, which is only one of RestClientException subclasses.
The address you're trying to reach (http://123.433.234.12/receive) is not valid address, therefore you can't get ANY response from it (no 200s but no 500s or 400s too). Try to catch RestClientException and print its message to see what is going on. Then you can write some code to manage such situations.
Moreover if that does not work, try to go step by step and check wether ResponseEntity is null and what it has in its body. That's what I'm doing when I try to understand some method ;=)
To pass a value to java I have used post method from AngularJs to my java code and it works just fine to do what i needed and my code is as the followoing :
#RequestMapping(value="/shelf", method=RequestMethod.POST, consumes="application/json")
public void getSelectedShelf(#RequestBody String shelf) throws JSONException {
logger.debug("Start uploading the files...");
logger.debug(shelf.toString());
JSONObject json;
try {
json = new JSONObject(shelf);
} catch (JSONException e) {
throw new IllegalArgumentException("Invalid JSON Payload: " + shelf, e);
}
selectedShelf= json.getLong("shelf");
logger.debug(selectedShelf+"");
logger.info("Json Payload = " + json);
}
, but at the end of my java method, I see the following error:
"org.thymeleaf.exceptions.TemplateInputException: Error resolving template "shelf", template might not exist or might not be accessible by any of the configured Template Resolvers
I don't have a template for /shelf and I don't want to have it as I just want to pass a value to my class. How can I resolve this problem ?
As #Tommy Schmidt has explained very well, if you don't want to return anything in a post method you should add #ResponseStatus(value = HttpStatus.OK) to the method.
I have simple request like
/*LOGIN*/
#FormUrlEncoded
#POST("v1/user/login") //your login function in your api
Call<LoginResponce> login(#Field("identity") String identity,
#Field("password") String password);
Which returns either LoginResponceobject if http code 200
{"token":"itwbwKay7iUIOgT-GqnYeS_IXdjJi","user_id":17}
Or Error Json, describes exact error, if something went wrong
{"status":4,"description":"user provided token expired"}
How can I handle error status in response?
I tried this, but it doesn't see JSON in raw text (doens't work). And doesn't seems to be nice solution.
mCallLoginResponse.enqueue(new Callback<LoginResponce>() {
#Override
public void onResponse(Response<LoginResponce> response, Retrofit retrofit) {
if (response.isSuccess()) {
registerWithToken(response.body().getToken());
} else { //some error in responce
Gson gson = new GsonBuilder().create();
ApiError mApiError = gson.fromJson(response.raw().body().toString(),
ApiError.class); //Exception here - no JSON in String
//todo error handling
}
}
To get access to the response body when you have an error code, use errorBody() instead of body(). Also, there is a string method on ResponseBody that you should use instead of toString.
Gson gson = new GsonBuilder().create();
try {
ApiError mApiError = gson.fromJson(response.errorBody().string(),ApiError.class);
} catch (IOException e) {
// handle failure to read error
}