I am using Below Custom Exception class in my project
public class BadRequestException extends WebApplicationException {
private static final long serialVersionUID = 1L;
private String message;
public BadRequestException(String message) {
super();
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
I have created a Mapper class also..
public class BadRequestExceptionMapper implements ExceptionMapper<BadRequestException> {
public Response toResponse(BadRequestException brexec) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(brexec.getResponse().getEntity()).type(MediaType.APPLICATION_JSON).build();
}
}
I am calling my service through a Servlet and the Exception is thrown by one of its method but i am not able to catch it in Servlet class.I have used below code to catch the exception..
try{
//Some Business logic then
service.path("restful").path("jwbservice/" + methodName + "/" + id).header("lid", lid).delete(String.class);
}
catch (BadRequestException ex) {
out.println(ex);
}
catch(Exception exe){
out.println(exe);
}
And the service method i have used this code in my Service class which will throw the exception.
#DELETE
#Path("/deleteLink/{id}")
#Produces(MediaType.APPLICATION_JSON)
public String deleteLink(#PathParam("id") int id, #HeaderParam("lid") String lid) throws BadRequestException {
if (id<= 0) {
throw new BadRequestException("Required Parameter: id");
}
//Some Business Logic
}
My Service throw the BadRequestException but in Servlet it is going to Exception catch not in BadRequestException Catch block.
Can any one know what i am doing wrong.
You will never get that exception in your servlet. This is because the servlet is effectively a REST client, and you are invoking a remote resource method to get some data. The resource call will either be successful (and some data will be mapped back), or it will fail and you will get no data (or a client side error).
On a side note, there is a problem in your server side exception mapper. You do not verify that the exception actually has a response entity before calling:
brexec.getResponse().getEntity()
In cases where the exception doesn't have a response the above code will cause a null pointer exception.
Some quick notes:
Exception classes already have a message property. You do not need to define an additional one
Your exception mapper needs to check for a non-existent response property, before trying to do something with it
The resource path in your servlet does not appear to match the server side path. I assume that is a copy/paste error.
Related
I have a Spring boot Get API which returns a 'User' object for a given user id.
#GetMapping( path = "/users/{userId}")
public ResponseEntity<User> getUser(
#PathVariable( userId )
Long id) throws CustomException {
//retuen user object
}
When someone passes a string value to the endpoint as userId this returns 'NumberFormatException'. Which gives an idea of the type of userId that is used on the side of the system. Is there a possibility that I can return a CustomException rather than returning a 'NumberFormatException'.
One option is to use type String for userId and then try to convert it to Long inside the method.
Other than that, Is there a better way to address this issue with any inbuild finalities of Spring Boot?
Yes you can by creating an exception advice class that can handle the runtime exceptions.
For example to handle the exceptions you must do the following:-
1- Create a custom class to use it as an exception response class.
public class ExceptionResponse {
private String message;
public ExceptionResponse(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
2- create an exception handler class to handle the thrown exceptions and add the exceptions that you want to handle.
#RestControllerAdvice
public class ExceptionCatcher {
#ExceptionHandler(NumberFormatException.class)
public ResponseEntity<ExceptionResponse> numberFormatExceptionHandler(NumberFormatException exception) {
ExceptionResponse response = new ExceptionResponse(exception.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
}
}
Or you can check this link to get more informations spring-rest-error-handling-example
You need to validate the inputs using #Validated annotations.
Please follow below link for more details:
https://howtodoinjava.com/spring-rest/request-body-parameter-validation/
I have a feign client like this
#FeignClient(name = "client")
public interface SomeClient {
#RequestLine("GET /?q={q}")
void execute(URI baseUrl, #Param("q") String q) throws SomeExceptionInMyCode;
}
Looking to this throws SomeExceptionInMyCode I'm asking myself when this exception will be thrown. There is no configuration for client defined, no error decoder. Exception looks like this.
public class SomeExceptionInMyCode extends Exception{
private final int statusCode;
private final String reason;
private final String body;
// getters and setters
}
Will there be an automatic attempt to decode HTTP response to this exception in case of failure? Or throws SomeExceptionInMyCode is useless and can be removed without any impact.
I searched inside my code and this exception is never created.
Will there be an automatic attempt to decode http response to this exception in case of failure?
Nope, it doesn't work like this and the SomeExceptionMyCode will not be thrown. The throws clause is useless. Even if the endpoint throws this exception from its implementation, it will be wrapped as a cause of FeignException.
The correct way to handle feign client exceptions is using Custom exception handling implementing ErrorDecoder:
public class StashErrorDecoder implements ErrorDecoder {
#Override
public Exception decode(String methodKey, Response response) {
if (response.status() >= 400 && response.status() <= 499) {
// return 4XX exception
}
if (response.status() >= 500 && response.status() <= 599) {
// return 5XX exception
}
}
}
At this point you can perform the custom exception creation and rethrowing.
An alternative solution is to use Spring-alike #RestControllerAdvice:
#RestControllerAdvice
public class ExceptionHandler {
#ExceptionHandler(FeignException.class)
public String handleFeignStatusException(FeignException e, HttpServletResponse response) {
// ...
}
}
What if the StashErrorDecoder throws a checked exception? That is allowed. In this case the throws clause in the interface surely helps. This way you can catch and handle the exception thrown by Feign. At least it should work this way.
I have a service class with #Async method and If it's calling method throwing any exception then the #ControllerAdvice will not call for global exception handling. But for other classes and services it will call advice and sending email properly.
#Service
public class FileScanServiceImpl implements FileScanService {
#Override
#Async
public void scanFileScheduler() throws MQException {
try{
messageProducer.putFileNameToMQ(fileName);
} catch (Exception e) {
ExceptionUtility.handleException(e, currentFile);
}
}
The ExceptionUtility is used for checking instance on exception and doing some functionality there and throwing custom exception.
public static void handleException(Exception e throws MQException {
String errMsg = "";
if (e instanceof MQException) {
// some functionality
throw new MQException(subject, errMsg);
}
}
And this is my #ControlleAdvice
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(MQException.class)
#ResponseBody
public void handleMQException(HttpServletRequest request, MQException ex) {
// send email
}
}
It there any solution for #Async which will call #ControllerAdvice for global exception, also the existing functionality will not break.
#ExceptionHandler was created to catch only "synchronous exceptions". If it had the ability to catch exceptions from asynchronous threads, then when several threads start and if any of them fail, the request to the server would be interrupted completely and the system could remain in an inconsistent state (due to many other active threads generated by this request)
For handling asynchronous exceptions Spring has the AsyncUncaughtExceptionHandler interface:
public class YourAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
#Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
// Your exception handling logic
}
}
More information can be found here in the Exceptions section: https://www.baeldung.com/spring-async
java 8, spring, rest
I am trying to capture the Response that comes from exception mapper, and do something with it in the caller which throws the exception. Thanks.
#Provider
public class CustomerExceptionHandler implements ExceptionMapper<CustomerException>
{
#Override
public Response toResponse(CustomerException exception)
{
return Response.status(Status.BAD_REQUEST).entity(CustomerException.getMessage()).build();
}
}
public class CustomerException extends Exception implements Serializable
{
private static final long serialVersionUID = 1L;
public CustomerException() {
super();
}
public CustomerException(String msg) {
super(msg);
}
public CustomerException(String msg, Exception e) {
super(msg, e);
}
}
public class ExceptionDemo{
public void getExceptionResponse(){
//do something
throw new CustomerException("Something is wrong");// CustomerExceptionHandler is going to return me a Response, how can I capture the response here?
//capture response and do something with it
}
}
I'm not sure ExceptionMappers work in the way you think they do.
When some code in the endpoint throws an exception, and this exception percolates all the way out of the endpoint and back into the container itself (Spring in this case), then the registered ExceptionMappers are consulted to see if they match the thrown exception, and the relevant one's public Response toResponse(T e) {} method is called to transform it into a Response.
The ExceptionMapper doen't get called as part of your endpoint code, and you won't be able to take action based on its resultant Response because it hasn't yet been called. You just need to throw the exception out of the endpoint.
I am using methods like this
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<UserWithPhoto> getUser(#RequestHeader(value="Access-key") String accessKey,
#RequestHeader(value="Secret-key") String secretKey){
try{
return new ResponseEntity<UserWithPhoto>((UserWithPhoto)this.userService.chkCredentials(accessKey, secretKey, timestamp),
new HttpHeaders(),
HttpStatus.CREATED);
}
catch(ChekingCredentialsFailedException e){
e.printStackTrace();
return new ResponseEntity<UserWithPhoto>(null,new HttpHeaders(),HttpStatus.FORBIDDEN);
}
}
And I want to return some text message when exception occurs but now I just return status and null object. Is it possible to do?
As Sotirios Delimanolis already pointed out in the comments, there are two options:
Return ResponseEntity with error message
Change your method like this:
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity getUser(#RequestHeader(value="Access-key") String accessKey,
#RequestHeader(value="Secret-key") String secretKey) {
try {
// see note 1
return ResponseEntity
.status(HttpStatus.CREATED)
.body(this.userService.chkCredentials(accessKey, secretKey, timestamp));
}
catch(ChekingCredentialsFailedException e) {
e.printStackTrace(); // see note 2
return ResponseEntity
.status(HttpStatus.FORBIDDEN)
.body("Error Message");
}
}
Note 1: You don't have to use the ResponseEntity builder but I find it helps with keeping the code readable. It also helps remembering, which data a response for a specific HTTP status code should include. For example, a response with the status code 201 should contain a link to the newly created resource in the Location header (see Status Code Definitions). This is why Spring offers the convenient build method ResponseEntity.created(URI).
Note 2: Don't use printStackTrace(), use a logger instead.
Provide an #ExceptionHandler
Remove the try-catch block from your method and let it throw the exception. Then create another method in a class annotated with #ControllerAdvice like this:
#ControllerAdvice
public class ExceptionHandlerAdvice {
#ExceptionHandler(ChekingCredentialsFailedException.class)
public ResponseEntity handleException(ChekingCredentialsFailedException e) {
// log exception
return ResponseEntity
.status(HttpStatus.FORBIDDEN)
.body("Error Message");
}
}
Note that methods which are annotated with #ExceptionHandler are allowed to have very flexible signatures. See the Javadoc for details.
Here is an alternative. Create a generic exception that takes a status code and a message. Then create an exception handler. Use the exception handler to retrieve the information out of the exception and return to the caller of the service.
http://javaninja.net/2016/06/throwing-exceptions-messages-spring-mvc-controller/
public class ResourceException extends RuntimeException {
private HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
public HttpStatus getHttpStatus() {
return httpStatus;
}
/**
* Constructs a new runtime exception with the specified detail message.
* The cause is not initialized, and may subsequently be initialized by a
* call to {#link #initCause}.
* #param message the detail message. The detail message is saved for later retrieval by the {#link #getMessage()}
* method.
*/
public ResourceException(HttpStatus httpStatus, String message) {
super(message);
this.httpStatus = httpStatus;
}
}
Then use an exception handler to retrieve the information and return it to the service caller.
#ControllerAdvice
public class ExceptionHandlerAdvice {
#ExceptionHandler(ResourceException.class)
public ResponseEntity handleException(ResourceException e) {
// log exception
return ResponseEntity.status(e.getHttpStatus()).body(e.getMessage());
}
}
Then create an exception when you need to.
throw new ResourceException(HttpStatus.NOT_FOUND, "We were unable to find the specified resource.");
Evaluating the error response from another service invocated...
This was my solution for evaluating the error:
try {
return authenticationFeign.signIn(userDto, dataRequest);
}catch(FeignException ex){
//ex.status();
if(ex.status() == HttpStatus.UNAUTHORIZED.value()){
System.out.println("is a error 401");
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
return new ResponseEntity<>(HttpStatus.OK);
}
return new ResponseEntity<>(GenericResponseBean.newGenericError("Error during the calling the service", -1L), HttpStatus.EXPECTATION_FAILED);