I have a Request Mapping -
@RequestMapping(\"/fetchErrorMessages\")
public @ResponseBody int fetchErrorMessages(@RequestParam(\"startTime\") String star
Suppose you have a custom Exception class NotFoundException
and its implementations something like this:
public class NotFoundException extends Exception {
private int errorCode;
private String errorMessage;
public NotFoundException(Throwable throwable) {
super(throwable);
}
public NotFoundException(String msg, Throwable throwable) {
super(msg, throwable);
}
public NotFoundException(String msg) {
super(msg);
}
public NotFoundException(String message, int errorCode) {
super();
this.errorCode = errorCode;
this.errorMessage = message;
}
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
public int getErrorCode() {
return errorCode;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public String getErrorMessage() {
return errorMessage;
}
@Override
public String toString() {
return this.errorCode + " : " + this.getErrorMessage();
}
}
Now you want to throw some exception from controller. If you throw a exception then you must catch it from a standard Error Handler class, say for example in spring they provide @ControllerAdvice
annotation to apply to make a class Standard Error Handler. When it is applied to a class then this spring component (I mean the class you annotated) can catch any exception thrown from controller. But We need to map exception class with proper method. So we defined a method with your exception NotFoundException
handler something like below.
@ControllerAdvice
public class RestErrorHandler {
@ExceptionHandler(NotFoundException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public Object processValidationError(NotFoundException ex) {
String result = ex.getErrorMessage();
System.out.println("###########"+result);
return ex;
}
}
You want to sent http status to internal server error(500), so here we used @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
. Since you used Spring-boot so you do not need to make a json string except a simple annotation @ResponseBody
can do that for you automagically.
Javax has a interface name as ExceptionMapper. Please refer the below code snippet, For every RuntimeException in your application it will map it to a Json Response entity.
public class RuntimeExceptionMapper implements ExceptionMapper <RuntimeException> {
@Override
public Response toResponse(RuntimeException exception) {
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setMessage(exception.getMessage);
if (exception== null) {
logger.error("Exception Details Not found");
} else {
return Response.status(Status.INTERNAL_SERVER_ERROR)
.entity(errorResponse )
.type(MediaType.APPLICATION_JSON)
.header("trace-id", "1234").build();
}
}
}
Create a custom exception.
public class SecurityException extends RuntimeException {
private static final long serialVersionUID = -7806029002430564887L;
private String message;
public SecurityException() {
}
public SecurityException(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Create a custom response entity.
public class SecurityResponse {
private String error;
public SecurityResponse() {
}
public SecurityResponse(String error) {
this.error = error;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
}
Create a ControllerAdvice with ExceptionHandler for custom exception, it will handle the custom exception, populate and return the custom response as below.
@ControllerAdvice
public class SecurityControllerAdvice {
@ExceptionHandler(SecurityException.class)
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public SecurityResponse handleSecurityException(SecurityException se) {
SecurityResponse response = new SecurityResponse(se.getMessage());
return response;
}
}
Throw the custom exception based on your condition.
throw new SecurityException("Date time format is invalid");
Now run and test you app. E.G. :
Spring provides a few ways to do this, some more sensible than others depending on your situation.
(Great tutorial here on several options. https://www.baeldung.com/spring-exceptions-json)
My favorite is this one because I want to send back a proper error message and an appropriate http response without creating a superclass or creating helper methods in a utility class or copying boilerplate everywhere.
If you want to inform the caller that the event caused an error (and in proper JSON), use Spring's ResponseStatusException. It gives you access to the httpReponse object so you can also send back a response other than 'ok'.
It wants an exception as one of it's parameters. For one of my scenarios I wanted to inform the caller that they were trying to register a user that already existed. Typically, looking up a user isn't supposed to throw an exception but in this case I created my own exception and I throw it back to the caller in a ResponseStatusException like so:
@PostMapping("/register")
public ResponseEntity register(@RequestBody AccountUserDto user) {
UserDetails userExists = userDetailsService.loadUserByEmail(user.getEmail());
if (userExists != null) {
UserExistsException exc = new UserExistsException("Error: Email address " + user.getEmail() + " is already in use.");
throw new ResponseStatusException(
HttpStatus.BAD_REQUEST, "User Exists", exc);
}
....(fall through and create user)
This is how I did it in my application:
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class ExceptionHandlingControllerAdvice {
@ExceptionHandler(ExecutionRestrictionViolationException.class)
public ResponseEntity<String> handleExecutionRestrictionViolationException(ExecutionRestrictionViolationException ex) {
return response("Invalid Query", ex.getMessage(), HttpStatus.UNPROCESSABLE_ENTITY);
}
private static String createJson(String message, String reason) {
return "{\"error\" : \"" + message + "\"," +
"\"reason\" : \"" + reason + "\"}";
}
private static ResponseEntity<String> response(String message,
String reason,
HttpStatus httpStatus) {
String json = createJson(message, reason);
return new ResponseEntity<>(json, httpStatus);
}
}
Explanation:
You create a controller Advice, mark it with a special annotation and define just like any other bean (in my case it was a java configuration, but it doesn't really matter)
For each Exception you would like to handle like this - define a handler that will generate a response in a format you want
Now this is only one way to work (its available in more recent spring boot versions) - but there are others:
All the methods I'm aware of (and even more) are listed here.
you can create NotFoundException
class with @ResponseStatus
annotation like below:
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public class NotFoundException extends RuntimeException {
public NotFoundException() {
}
public NotFoundException(String message) {
super(message);
}
}