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
Related
I have a Spring Boot server that listens on endpoint. I accept #RequestBody as an object:
class Body {
private String name;
}
I want it to accept requests like:
{
"name": "some_name"
}
However, it also accepts:
{
"name": "some_name",
"dummy key":"dummy key value"
}
In that case I want it to throw error. How can I achieve it?
You can do this in the controller when saving:
#PostMapping("/add")
public ResponseEntity<Body> registerUser(#Valid #RequestBody Body saveUser) {
Body createdUser = userService.save(saveUser);
return new ResponseEntity<>(createdUser, HttpStatus.CREATED);
}
When Spring finds an argument annotated with #Valid, it automatically validates the argument and throws an exception if the validation fails.
or you can do this as well:
In the application.properties
spring.jackson.deserialization.fail-on-unknown-properties=true
This helps us to make deserialization fail on unknown properties and throw an exception which we can handle using handleHttpMessageNotReadable
Create controller advice to handle exceptions
#ControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(
HttpMessageNotReadableException ex, HttpHeaders headers,
HttpStatus status, WebRequest request) {
return new ResponseEntity("Your Response Object",
HttpStatus.INTERNAL_SERVER_ERROR);
}
}
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 am getting NotFoundException while trying to implement custom exception handling in spring-boot rest application.
The code was working fine when I was using MVC (using #ControllerAdvice) annotations but not sure when I am sending a data which is violating the constraint mentioned in entity(pojo class) it is throwing only NotFoundException (for all validation failure) but not the MethodViolationException or ConstraintViolationException
I am not able to send the message for that particular violation.
Not sure where I am making this mistake. Please help
Code:
#POST
#Path("/customers/add")
public Response addCustomer(#Valid customer cust)
{
// Rest of the code
}
POJO:
#Entity
#Table(name="cust")
public class Customer
{
#NotNull
#Size(min=1,max=50,message ="invalid name")
String name;
}
Exception Handler:
#Provider
public class CustomHandler implements ExceptionMapper<Exception>
{
public Response toResponse(Exception ex)
{
if(ex instanceOf ConstraintViolationException)
{
Do something
}
}
**UPDATE 1
If I enable the send_error_in_response i am getting the message for this but not sure why my custom exception handler is not able to catch this exception and only throwing NotFoundException
Try Handling Exception Using:
#ControllerAdvice
#RestController
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(StudentNotFoundException)
public final ResponseEntity<ErrorDetails> handleUserNotFoundException(StudentNotFoundException ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(),
request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
}
For more information you might want to refer http://www.springboottutorial.com/spring-boot-validation-for-rest-services
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 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.