Spring Rest Client Exception Handling - java

I am using spring RestTemplate to consume rest services(exposed in spring rest). I am able to consume success scenarios. But for negative scenarios, service returns error messages and error codes. I need to show those error messages in my web page.
For e.g. for invalid request, service throws HttpStatus.BAD_REQUEST with proper messages. If i put try-catch block it goes to catch block and I am not able to get ResponseEntity object.
try {
ResponseEntity<ResponseWrapper<MyEntity>> responseEntity = restTemplate.exchange(requestUrl, HttpMethod.POST, entity,
new ParameterizedTypeReference<ResponseWrapper<MyEntity>>() {
});
responseEntity.getStatusCode();
} catch (Exception e) {
//TODO How to get response here, so that i can get error messages?
e.printStackTrace();
}
How to get ResponseWrapper for exception case?
I read about CustomRestTemplate and ResponseExtractor from here but could not decide which one is best for my case.

I found solution. HttpClientErrorException worked for me.
It has e.getResponseBodyAsString() function which returns ResponseBody.

You might want to distinguish between:
HttpClientErrorException (HTTP-Status >=400) or HttpServerErrorException (HTTP-Status >= 500) or even RestClientException.
Further on there is a good doc about defining your own ErrorHandler, see this link

Related

Error while I configure my personal message in JWT access

I'm trying to handle JWT requests when they are expired or not valid...
But when I tryning to catch the error I get this:
Unable to handle the Spring Security Exception because the response is already committed.
and I can't use a code like this:
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid JWT Token");
I still get error 500 in postman
Your question is not clear enough to decide...
but I suppose you just need two things:
1- create a #ControllerAdvice class for your exceptions handling, this class should handle the responses displayed in postman when an exception occurred.
Read about Spring Exception Handling https://javatodev.com/microservices-exception-handling/
and here are some outlines of that class:
#ControllerAdvice
public class ExceptionsController extends RunTimeExceptions{
#ExceptionHandler(Exception.class)
public final ResponseEntity<String> handleAllExceptions(){
return new ResponseEntity<>(response, HttpStatus.OK);
}
}
2- second of all is very obvious, you should surround your statements with try... catch
and throw a new exception in the catch clause to invoke the ExceptionHandler class that we created.

Custom WebApplicationExceptions in jaxrs caught by ExceptionMapper show up in server log

I use the following Exceptionmapper to map WebApplicationExceptions in my jaxrs rest api to responses.
#Provider
public class ErrorHandler implements ExceptionMapper<WebApplicationException> {
#Override
public Response toResponse(WebApplicationException e) {
int status = e.getResponse().getStatus();
JsonObject errorResponse = Json.createObjectBuilder()
.add("status", status)
.add("message", e.getMessage())
.build();
return Response.status(status)
.entity(errorResponse)
.type(MediaType.APPLICATION_JSON)
.build();
}
}
This works fine and it does exactly what it should do, but when I throw custom errors, for example throw new NotFoundException("custom message"); the stacktrace shows up in my server log. Can anyone explain this? Or does anyone know of a solution?
TL;DR;
For some reason when I throw WebApplicationExceptions from my jax-rs code, my ExceptionMapper handles the error but still throws it so it shows up in the server log.
Any solutions?
I've found the origin of this problem and managed to solve it.
From the JAX-RS spec
When choosing an exception mapping provider to map an exception, an implementation MUST use the provider whose generic type is the nearest superclass of the exception.
In my ExceptionMapper I used the WebApplicationException, so every error would be mapped. The problem is that WebApplicationException is not the nearest superclass of (e.g.) NotFoundException. There is a ClientErrorException superclass inbetween. When I changed my mapper to that class the problem was solved.
Since I only want to map client errors to Json responses this solution works fine for me.

How could we use #ExceptionHandler with spring web flux?

In spring web we could use annotation #ExceptionHandler for handling server and client errors for controllers.
I've tried to use this annotation with web-flux controller and it still worked for me, but after some investigation I've found out here
The situation with Spring Web Reactive is more complicated. Because
the reactive streams are evaluted by a different thread than the one
that executes the controllers method, the exceptions won’t be
propagated to the controller thread automatically. This means that the
#ExceptionHandler method will work only for exceptions that are thrown
in the thread that handles the request directly. Exceptions thrown in
the stream will have to be propagated back to the thread if we want to
use the #ExceptionHandler feature. This seems like a bit of a let down
but at the time of writing this Spring 5 is still not released so
error handling might still get better.
So my question is how to propagate back exception to the thread. Is there a good example or article about using #ExceptionHandler and Spring web flux?
Updated:
From spring.io it looks like it's supported, but still lack general understanding
Thanks,
Now it is possible to use the #ExceptionHandler as well as #RestControllerAdvice or even #ControllerAdvice in Spring WebFlux.
Example:
Add the webflux dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
Create your class ExceptionHandler
#RestControllerAdvice
public class ExceptionHandlers {
private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionHandlers.class);
#ExceptionHandler(Exception.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String serverExceptionHandler(Exception ex) {
LOGGER.error(ex.getMessage(), ex);
return ex.getMessage();
}
}
Create a Controller
#GetMapping(value = "/error", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Mono<String> exceptionReturn() {
return Mono.error(new RuntimeException("test error"));
}
Example extracted here:
https://ddcode.net/2019/06/21/spring-5-webflux-exception-handling/
You can use #ExceptionHandler annotated methods to handle errors that happen within the execution of a WebFlux handler (e.g., your controller method). With MVC you can indeed also handle errors happening during the mapping phase, but this is not the case with WebFlux.
Back to your exception propagation question, the article you're sharing is not accurate.
In reactive applications, the request processing can indeed hop from one thread to another at any time, so you can't rely on the "one thread per request" model anymore (think: ThreadLocal).
You don't have to think about exception propagation or how threads are managed, really. For example, the following samples should be equivalent:
#GetMapping("/test")
public Mono<User> showUser() {
throw new IllegalStateException("error message!");
}
#GetMapping("/test")
public Mono<User> showUser() {
return Mono.error(new IllegalStateException("error message!"));
}
Reactor will send those Exceptions as error signals as expected in the Reactive Streams contract (see the "error handling" documentation section for more on that).
not an exact answer to the original question, but a quick way to map your exceptions to http response status is to throw org.springframework.web.server.ResponseStatusException / or create your own subclasses...
Full control over http response status + spring will add a response body with the option to add a reason.
{
"timestamp": 1529138182607,
"path": "/api/notes/f7b.491bc-5c86-4fe6-9ad7-111",
"status": 400,
"error": "Bad Request",
"message": "For input string: \"f7b.491bc\""
}
The following global error handler did the trick for me:
import org.springframework.web.server.ResponseStatusException;
#Slf4j
#RestControllerAdvice
#ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public class MyCustomReactiveErrorHandling {
#ExceptionHandler(MyCustomNotFoundException.class)
public void handleMyCustomException(MyCustomNotFoundException ex) {
throw new ResponseStatusException(404, "Data not found!", ex);
}
}
Throwing my exceptions returns the correct http status code at the rest service.

When HttpStatusCodeException exception raised?

when i use below code , what is the case to get HttpStatusCodeException exception .
ResponseEntity<Object> response =
restTemplate.exchange(builder.build().encode().toUri(), HttpMethod.GET, entity, Object.class);
Please can anyone help out ????????
According to documentation there are two types of HttpStatusCodeException HttpClientErrorException and HttpServerErrorException.
The former is thrown when an HTTP 4xx is received.
The latter is thrown when an HTTP 5xx is received.
so you can just use ResponseEntity.BodyBuilder.status(505) for example to raise an HttpServerErrorException in your
exhange(...) method of org.springframework.web.client.RestTemplate class was added in 3.1.0.RELEASE of spring-web library.
This method throws RestClientException which covers client (4_xx) and server (5_xx) side http code errors. But RestClientException doesn't offer getStatusCode(), getResponseAsString(), etc... methods.
HttpsStatusCodeException is the child of RestClientException which is doing same thing but with additional methods like getStatusCode(), getResponseAsString(), etc.
HttpClientErrorException child of HttpsStatusCodeException and only entertain client error (4_xx) not the server error.
HttpServerErrorException child of HttpsStatusCodeException and only entertain server error (5_xx) not the client error.
HTTP Status Codes are responses from the server, therefore if you have control of the server then you could make it return whichever errors you want. If you don't have control of the server then you could try sending bad/invalid requests so that the server will complain.
Something like this on your server side:
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity getAnError() {
// complain!
return ResponseEntity.status(HttpStatus.FORBIDDEN);
}

JAX-RS service throwing a 404 HTTPException but client receiving a HTTP 500 code

I have a RESTful resource, which calls a EJB to make a query. If there is no result from the query, the EJB throws a EntityNotFoundException. In the catch block, it will be thrown a javax.xml.ws.http.HTTPException with code 404.
#Stateless
#Path("naturezas")
public class NaturezasResource {
#GET
#Path("list/{code}")
#Produces(MediaType.APPLICATION_JSON)
public String listByLista(
#PathParam("code") codeListaNaturezasEnum code) {
try {
List<NaturezaORM> naturezas = this.naturezaSB
.listByListaNaturezas(code);
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(naturezas);
} catch (EntityNotFoundException e) { // No data found
logger.error("there is no Natures with the code " + code);
throw new HTTPException(404);
} catch (Exception e) { // Other exceptions
e.printStackTrace();
throw new HTTPException(500);
}
}
}
When I call the Rest Service with a code for which there are no results, the log message inside the EntityNotFoundException catch block is printed. However, my client receives a HTTP code 500 instead a 404. Why am I not receiving a 404 code?
Thanks,
Rafael Afonso
javax.xml.ws.http.HTTPException is for JAX-WS. JAX-RS by default doesnt know how to handle it unless you write an ExceptionMapper for it. So the exception bubbles up to the container level, which just sends a generic internal server error response.
Instead use WebApplicationException or one of its subclasses. Here a list of the exceptions included in the hierarchy, and what they map to (Note: this is only in JAX-RS 2)
Exception Status code Description
-------------------------------------------------------------------------------
BadRequestException 400 Malformed message
NotAuthorizedException 401 Authentication failure
ForbiddenException 403 Not permitted to access
NotFoundException 404 Couldn’t find resource
NotAllowedException 405 HTTP method not supported
NotAcceptableException 406 Client media type requested
not supported
NotSupportedException 415 Client posted media type
not supported
InternalServerErrorException 500 General server error
ServiceUnavailableException 503 Server is temporarily unavailable
or busy
You can find them also in the WebApplicationException link above. They will fall under one of the direct subclasses ClientErrorException, RedirectionException, or ServerErrorException.
With JAX-RS 1.x, this hierarchy doesn't exist, so you would need to do something like #RafaelAlfonso showed in a comment
throw new WebApplicationException(Response.Status.NOT_FOUND);
There are a lot of other possible constructors. Just look at the API link above

Categories

Resources