I am developing a project using Spring REST web services, where I need to show graceful error messages when an exception/error occurs. I followed this tutorial (http://www.javacodegeeks.com/2013/02/exception-handling-for-rest-with-spring-3-2.html) for exception handling using SpringREST. I get the proper output when there is no exception/error i.e. in form of an XML. The issue arises when an exception occurs. Here is part of the code base where an exception occurs if I do not pass the testId in
localhost:8080/test?testId=
The class outputs a response in form of a XML, so when an exception occurs, instead of showing the error message as figure 1 below, it shows error message as figure 2. If I do "View Page Source", I get the correct exception message (as figure 1). But I need the exception message directly. Could anyone, please suggest a solution?
#RequestMapping(value = "/test",
method = RequestMethod.GET,
produces = "application/xml")
public #ResponseBody String testResource(
#RequestParam(value="testId", required=true) String testId)
throws CustomRestException{
if (testId == null || testId.equals(""))
{
LOG.error( "testResource(): " + TestUtilsException.NULL_TEST_ID_ERROR_MSG );
//The error message is: The test Id is required and cannot be null or empty
throw new CustomRestException(TestUtilsException.NULL_TEST_ID_ERROR_MSG);
}
}
Figure 1
Figure 2
Other helper classes:
#ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
public RestResponseEntityExceptionHandler() {
super();
}
#ExceptionHandler(value = { CustomRestException.class })
#ResponseBody
protected ResponseEntity<Object> handleNotFound(final RuntimeException ex, final WebRequest request) {
final String bodyOfResponse = ex.getMessage();
return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request);
}
}
public class CustomRestException extends RuntimeException {
public CustomRestException() {
super();
}
public CustomRestException(final String message, final Throwable cause) {
super(message, cause);
}
public CustomRestException(final String message) {
super(message);
}
public CustomRestException(final Throwable cause) {
super(cause);
}
}
The #ControllerAdvice approach should work, although I don't think there's any need for the base class - you can just use #ExceptionHandler with Spring 4. But you are returning a response body that cannot be converted to Xml (it's a plain String), so you are getting an empty response and probably a 405 instead of a 500. If you want an Xml response you have to provide a body that can be converted (or else provide an HttpMessageConverter that can do it).
Consider doing this.
public class BaseController{
#ExceptionHandler(value = { CustomRestException.class })
protected #ResponseBody ResponseEntity<ErrorResponse > handleNotFound(final RuntimeException ex, final WebRequest request) {
System.out.println("is executed in handler");
final String bodyOfResponse = ex.getMessage();
return new ResponseEntity<ErrorResponse >(new ErrorResponse (bodyOfResponse), null, HttpStatus.NOT_FOUND);
}
}
In your controller do this.
#Controller
public class HomeController extends BaseController {//Your code here}
And create this class.
#XmlRootElement
public class ErrorResponse {
public String error;
}
Finally add to your class the following code
if (testId == null || testId.equals(""))
{
throw new CustomRestException("DD");
}
That will create an XML response as follows.
This XML file does not appear to have any style information associated
with it. The document tree is shown below.
<successResponse> <error>DD</error> </successResponse>
This will handle all the exception an is not needed to add #ControllerAdvice, that seems need to add their own MessageConverters that is why the answer is not converted to XML, I read that here.
I added ,produces = "application/xml" and remove it, and is still working as I think you want. Please let me know if this was useful.
Related
Hi I am new to spring boot.when I try submit the request from the postman it is returning org.springframework.transaction.TransactionSystemException with HttpStatus code : 500 if invalid it throwing the javax.validation.ConstraintViolationException in the server.
Can any one share the best solution to handle these exceptions?
I tried in controller with the below code:
#ExceptionHandler(ConstraintViolationException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
ResponseEntity<String> handleConstraintViolationException(ConstraintViolationException e) {
return new ResponseEntity<>("not valid due to validation error: " + e.getMessage(), HttpStatus.BAD_REQUEST);
}
But I want the response to be send in Json format with customized error message How can I achieve it ?
And also wanted to avoid the exception handling code in the controller.Is there any better way?
Each of such exceptions must be handled separately like below
#RestControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler ({ConstraintViolationException.class})
#ResponseStatus(HttpStatus.BAD_REQUEST)
protected ResponseEntity<Object> handleConstraintViolationException(
ConstraintViolationException e) {
return new ResponseEntity<>(new JsonErrorResponse(e.getMessage()), HttpStatus.BAD_REQUEST);
}
}
If you want to create your own response object then u can use the below way in the RestExceptionHandler class.
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(
HttpMessageNotReadableException e, HttpHeaders headers, HttpStatus status, WebRequest request) {
return new ResponseEntity<>(new CustomErrorResponse(e.getMessage()), HttpStatus.BAD_REQUEST);
}
private class CustomErrorResponse {
String message;
public CustomErrorResponse() {
}
public CustomErrorResponse(String message) {
super();
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
You can use #ControllerAdvice. It's a special component to handle error across the hole application. Here I show you a example:
#ControllerAdvice
public class CustomErrorHandler{
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<String> handleContraintViolationException() {
// Your custom response
}
}
The pro is that you have a different class that handle the exceptions in all the application.
500 is a server problem. It means your server encounter a problem while executing the code. Specifically it's ConstraintViolationException which means you have a constraint when inserting your data in the database.
Example :
Imagine we have an entity that has a unique field
#Entity
user {
UUID id;
#column
String name;
}
and when creating the entity you have made a uniquness contrainte in database :
ALTER TABLE USER
ADD CONSTRAINT UC_name UNIQUE (name);
Here if you try to insert two user with the same name you would receive a ConstraintViolationException
I'd like to do custom exception handling for a REST API.
This is the code I have.
Controller Endpoint
#PatchMapping(value="/customer/name", produces = "application/json")
public ResponseEntity<Customer> updateName(
#RequestParam(value="customerId") Long customerId,
#RequestParam(value="name") String name){
customerRepository.updateCustomerName(customerId, name);
Customer updatedCustomer = customerRepository.findCustomer(customerId);
return new ResponseEntity<Customer>(updatedCustomer, HttpStatus.OK);
}
Custom Exception Handling Class
#ControllerAdvice
public class CustomRestExceptionHandler extends ResponseEntityExceptionHandler{
#ExceptionHandler(value = {Exception.class})
public ResponseEntity<Object> handleAll(Exception ex, WebRequest request) {
return new ResponseEntity<>(
ex, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
If I force an error inside the endpoint method (such as adding the null pointer exception below), it will correctly enter the handleAll method and return the custom error.
String x = null;
int y = x.length();
But, if instead of that, I generate the error by going to Postman and pass a String instead of a Long in the customerId parameter, it doesn't enter the custom error class.
In fact, it never enters the controller method.
How to make the custom error class catch and display custom error for that as well?
thanks
try to override handleMethodArgumentTypeMismatch
#ExceptionHandler({MethodArgumentTypeMismatchException.class})
public ResponseEntity<Object> handleMethodArgumentTypeMismatch( MethodArgumentTypeMismatchException ex, WebRequest request) {
return ResponseEntity
}
I'm having a peculiar situation with my #ControllerAdvice annotated ExceptionHandler in Spring Boot 1.5.3. It catches any exceptions default Exceptions, but if I throw a custom exception it does not fire.
The ExceptionHandler:
#ControllerAdvice
public class ResponseEntityExceptionHandler {
#ExceptionHandler({ HttpMessageNotReadableException.class })
protected ResponseEntity<ErrorModel> handleInvalidJson(RuntimeException e, WebRequest request) {
return new ResponseEntity<ErrorModel>(new ErrorModel().message("Could not parse JSON."), HttpStatus.BAD_REQUEST);
}
#ExceptionHandler({ NumberFormatException.class })
protected ResponseEntity<ErrorModel> handleInvalidRequest(RuntimeException e, WebRequest request) {
return new ResponseEntity<ErrorModel>(new ErrorModel().message("Invalid request parameter."), HttpStatus.BAD_REQUEST);
}
#ExceptionHandler({ CannotCreateTransactionException.class })
protected ResponseEntity<ErrorModel> handleTransactionCreationException(RuntimeException e, WebRequest request) {
return new ResponseEntity<ErrorModel>(new ErrorModel().message("Error connecting to the database, please make sure it is still available."), HttpStatus.BAD_REQUEST);
}
#ExceptionHandler({ NotFoundException.class })
protected ResponseEntity<ErrorModel> handleApiException(RuntimeException e, WebRequest request) {
return new ResponseEntity<ErrorModel>(new ErrorModel().message(e.getMessage()), HttpStatus.NOT_FOUND);
}
}
The top 3 Exceptions all get caught and handled as they are supposed to, but the bottom Exception gets handled by the default Spring-Boot ExceptionHandler. It is a custom Exception that I throw inside a Controller:
public ResponseEntity<?> deleteActor(#ApiParam(value = "Used to identify a single actor.", required = true) #PathVariable("actor_id") Integer actorId, #RequestHeader("Accept") String accept) throws Exception {
Actor actor = actorRepository.findOne(actorId);
if (actor == null) {
throw new NotFoundException(404, "Not found");
}
actorRepository.delete(actorId);
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}
I've tried throwing one of the top Exceptions like this:
public ResponseEntity<?> readActor(#ApiParam(value = "Used to identify a single actor.", required = true) #PathVariable("actor_id") Integer actorId, #RequestHeader("Accept") String accept) throws Exception {
Actor actor = actorRepository.findOne(actorId);
if (actor == null) {
throw new NumberFormatException("");
}
return new ResponseEntity<Actor>(actor, HttpStatus.OK);
}
and these get handled just fine...
The tomcat logs also show this:
2017-06-05 11:30:20.080 INFO 9076 --- [ main] .m.m.a.ExceptionHandlerExceptionResolver : Detected #ExceptionHandler methods in responseEntityExceptionHandler
The Exception:
public class NotFoundException extends ApiException {
private int code;
public NotFoundException (int code, String msg) {
super(code, msg);
this.code = code;
}
}
The exception inherits from this baseclass:
public class ApiException extends Exception{
private int code;
public ApiException (int code, String msg) {
super(msg);
this.code = code;
}
}
Any ideas about why the custom Exception avoids detection by the ExceptionHandler?
I would be happy to provide additional information should that be necessary.
For this particular case the answer is to use Exception instead of RuntimeException, since NotFoundException does only inherit from Exception.
Further notable things:
To catch all exceptions one can use an #ExceptionHandler(Exception.class)
If using common names for exceptions, always check if you have imported the right one.
For me the CustomException was not getting caught by #ControllerAdvice method. I searched for hours and finally the issue got resolved on Updating the project.
Right click on your project -> Maven -> Update project.
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);
I have created a Spring MVC REST service using Bean Validation 1.2 with the following method:
#RequestMapping(value = "/valid")
public String validatedMethod(#Valid ValidObject object) {
}
If object isn't valid, Tomcat informs me that The request sent by the client was syntactically incorrect. and my validatedMethod is never called.
How can I get the message that was defined in the ValidObject bean? Should I use some filter or interceptor?
I know that I can rewrite like below, to get the set of ConstraintViolations from the injected Validator, but the above seems more neat...
#RequestMapping(value = "/valid")
public String validatedMethod(ValidObject object) {
Set<ConstraintViolation<ValidObject>> constraintViolations = validator
.validate(object);
if (constraintViolations.isEmpty()) {
return "valid";
} else {
final StringBuilder message = new StringBuilder();
constraintViolations.forEach((action) -> {
message.append(action.getPropertyPath());
message.append(": ");
message.append(action.getMessage());
});
return message.toString();
}
}
I believe a better way of doing this is using ExceptionHandler.
In your Controller you can write ExceptionHandler to handle different exceptions. Below is the code for the same:
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public ValidationFailureResponse validationError(MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
final List<FieldError> fieldErrors = result.getFieldErrors();
return new ValidationFailureResponse((FieldError[])(fieldErrors.toArray(new FieldError[fieldErrors.size()])));
}
When you send a bad request to the Controller, the validator throws an exception of type MethodArgumentNotValidException. So the ideal way would be to write an exception handler to specifically handle this exception.
There you can create a beautiful response to tell the user of things which went wrong.
I advocate this, because you have to write this just once and many Controller methods can use it. :)
UPDATE
When you use the #Valid annotation for a method argument in the Controller, the validator is invoked automatically and it tries to validate the object, if the object is invalid, it throws MethodArgumentNotValidException.
If Spring finds an ExceptionHandler method for this exception it will execute the code inside this method.
You just need to make sure that the method above is present in your Controller.
Now there is another case when you have multiple Controllers where you want to validate the method arguments. In this case I suggest you to create a ExceptionResolver class and put this method there. Make your Controllers extend this class and your job is done.
Try this
#RequestMapping(value = "/valid")
public String validatedMethod(#Valid ValidObject object, BindingResult result) {
StringBuilder builder = new StringBuilder();
List<FieldError> errors = result.getFieldErrors();
for (FieldError error : errors ) {
builder.append(error.getField() + " : " + error.getDefaultMessage());
}
return builder.toString();
}
When you use #Valid and doing bad request body Spring handle MethodArgumentNotValidException
You must create special class and extend ResponseEntityExceptionHandler and override handleMethodArgumentNotValid
Example
#ControllerAdvice
public class ControllerExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(UserExistException.class)
public ResponseEntity<Object> handleUserExistException(
UserExistException e, WebRequest request) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("timestamp", LocalDateTime.now());
body.put("status", HttpStatus.BAD_REQUEST.value());
body.put("error", HttpStatus.BAD_REQUEST.getReasonPhrase());
body.put("message", e.getMessage());
body.put("path", request.getDescription(false).replace("uri=", ""));
return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST);
}
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("timestamp", LocalDateTime.now());
body.put("status", HttpStatus.BAD_REQUEST.value());
body.put("error", HttpStatus.BAD_REQUEST.getReasonPhrase());
body.put("path", request.getDescription(false).replace("uri=", ""));
return new ResponseEntity<>(body, headers, status);
}
}
The answer by #dharam works.
For users at Spring v4.3, Here's a nice implementation which uses a Custom Exception class to handle exception by type.
#RestControllerAdvice
public class CustomExceptionClass extends ResponseEntityExceptionHandler{
#ExceptionHandler(value = MethodArgumentNotValidException.class)
public ResponseEntity<Object> handleException(MethodArgumentNotValidException ex, WebRequest req){
// Build your custom response object and access the exception message using ex.getMessage()
}
}
This method will enable handling all #Valid exceptions across all of your #Controller methods in a consolidated way