I have a method which should throw an exception if the input is blank. The input, in this case, a list, will throw an exception if it is empty. I have debugged this using JUnit and can confirm that passing in [] would throw the exception. In my front end application however, passing in an empty list still shows the response status as 200 (as seen in the developer console). Shouldn't throwing an exception generate a response status of 500? At least that's what I'm looking for, so that way I can respond with an error message to the user. How can I get the below to generate a response status of 500? I'm using spring framework.
public Boolean validate(List<HashMap<String, Object>> myList) throws MyException{
Boolean hasErrors = false;
if(!myList.isEmpty()){
//logic goes here
....
} else{
hasErrors = true;
throw new MyException("myList shouldn't be empty ");
}
return hasErrors;
}
Exception class here:
public class MyException extends Exception {
public MyException() { super(); }
public MyException(String message) { super(message); }
public MyException(String message, Throwable cause) { super(message, cause); }
public MyException(Throwable cause) { super(cause); }
}
Any help would be appreciated.
I would suggest that you use proper exception handling using Spring MVC, that way, it would be much more clear on what should be returned as status code when throwing an exception.
For example, you can do something like this in your exception class:
#ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR, reason="myList shouldn't be empty")
public class MyException extends Exception {
public MyException() { super(); }
public MyException(String message) { super(message); }
public MyException(String message, Throwable cause) { super(message, cause); }
public MyException(Throwable cause) { super(cause); }
}
You can use a #ControllerAdvice to handle exception that you throw then specify what return status you want to the client.
Here is an example that I have that I test to see if the file the user is uploading is too large for the website and return a bad request (html code 400).
The toExceptionResponeString is just a method that converts my exception to a json string, any string will do for the return.
You can use your MyException class in lieu of the MaxUploadSizeExceededException and handle it from there.
#ControllerAdvice
public class ExceptionController extends ResponseEntityExceptionHandler {
#Autowired
private ApplicationLogService appLogService;
#ExceptionHandler(value = { MaxUploadSizeExceededException.class })
protected ResponseEntity<Object> handleMaxUploadSizeExceededException(Exception ex, WebRequest request) {
MaxUploadSizeExceededException musee = (MaxUploadSizeExceededException)ex;
SizeLimitExceededException slee = musee.getCause() instanceof SizeLimitExceededException ? (SizeLimitExceededException) musee.getCause() : null;
Long maxSize = slee == null ? musee.getMaxUploadSize() : slee.getPermittedSize();
Long actualSize = slee == null ? Long.valueOf(request.getHeader("Content-Length")) : slee.getActualSize();
PortalException pre = PortalException.fileSizeTooLarge(maxSize, actualSize);
try {
appLogService.addExceptionEntry(pre.getMessage());
} catch (Exception e) { }
return handleExceptionInternal(ex, toExceptionResponseString(pre), new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
}
}
Related
I am using Spring Boot 3 to develop a simple Rest API and want to handle exceptions using #RestControllerAdvice, but my code is throwing a 500 error even if the code in #exceptionHandler is throwing a 404 error code. I verified in debugging that execution reaches the ExceptionHandler method, but the default exception is still thrown.
Below is sample code for:
#RestControllerAdvice
public class ControllerExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(ResourceNotFoundException.class)
#ResponseStatus(code = HttpStatus.NOT_FOUND)
public Messsage resourceNotFoundException(ResourceNotFoundException exception, WebRequest request) {
return new Messsage(HttpStatus.NOT_FOUND.value(), exception.getMessage(), LocalDateTime.now());
}
#ExceptionHandler(Exception.class)
#ResponseStatus(code = HttpStatus.NOT_FOUND)
public Messsage globalException(Exception exception, WebRequest request) {
return new Messsage(HttpStatus.NOT_FOUND.value(), exception.getMessage(), LocalDateTime.now());
}
}
Try to remove ResponseEntityExceptionHandler .
which some ExceptionHandler has defined and it may catch exception before you custom exceptionHandler.
I had made some unit test here is code sample;
Application
#SpringBootApplication
public class AccountApplication {
public static void main(String[] args) {
SpringApplication.run(AccountApplication.class);
}
}
Controller
/**
* #author pengpeng
* #description
* #date 2023/2/13
*/
#RequestMapping
#RestController
public class AccountController {
#GetMapping("/test/{args}")
public String testError(#PathVariable ("args") String args) throws IllegalAccessException {
if (args .equals("ill")){
throw new IllegalAccessException();
} else if (args.equals("exception")) {
throw new RuntimeException();
}else {
return "success";
}
}
}
exceptionHandler
/**
* #author pengpeng
* #description
* #date 2023/2/13
*/
#RestControllerAdvice
public class ControllerException extends ResponseEntityResultHandler {
public ControllerException(List<HttpMessageWriter<?>> writers, RequestedContentTypeResolver resolver) {
super(writers, resolver);
}
public ControllerException(List<HttpMessageWriter<?>> writers, RequestedContentTypeResolver resolver, ReactiveAdapterRegistry registry) {
super(writers, resolver, registry);
}
#ExceptionHandler
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String illegalFail(IllegalAccessException exception){
return "illegalFail";
}
#ExceptionHandler
#ResponseStatus(HttpStatus.BAD_GATEWAY)
public String fail(Exception exception){
return "fail";
}
}
after application start use the default application port to initiates HTTP call
http
127.0.0.1/test/ill
127.0.0.1/test/exception
127.0.0.1/test/success
It is likely that another exception is being thrown inside the exception handler method which is leading to the 500 error. To troubleshoot this, you can add additional logging to your exception handler method to see what is causing the 500 error. it is better to use try and catch block in resourceNotFoundException and globalException methods.
I have a ControllerExceptionHandler with ControllerAdvise and when in application is thrown generalException or customException then i except to catch it in this ControllerExceptionHandler . But it doesn't happen. It looks very simple, read many sites , but it's not triggering.
Don't know where is the problem.
It's ControllerExceptionHandler.class
#ControllerAdvice
public class ControllerExceptionHandler extends ResponseEntityExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(ControllerExceptionHandler.class);
#ExceptionHandler(Exception.class)
public final ResponseEntity<?> handleGeneralErrors(Exception e, UserPrincipal userPrincipal) {
if (e instanceof ClientAbortException) {
LOGGER.error("REST client abort: {}", e.getMessage());
} else {
LOGGER.error("REST controller error.", e);
}
//somelogic
return new ResponseEntity<>(responseObject, HttpStatus.INTERNAL_SERVER_ERROR);
}
#ExceptionHandler(MyCustomException.class)
public final ResponseEntity<?> handleCustomErrors(MyCustomException e, UserPrincipal userPrincipal) {
LOGGER.error("MyCustomException error.", e);
return new ResponseEntity<>(
responseObject,
HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Controller.class
#RestController
#RequestMapping(path = "/rest/test", produces = MediaType.APPLICATION_JSON_VALUE)
public class Controller {
private static final Logger LOGGER = LoggerFactory.getLogger(Controller .class);
#Autowired
private SomeLogicClass someLogicClass;
#GetMapping("/check")
#ResponseBody
public ResponseEntity<List<City>> list(UserPrincipal userPrincipal) throws Exception {
//Some logic
return ResponseEntity.ok(someLogicClass.handleRequest());
}
SomeLogicClass.class
#Service
public class SomeLogicClass{
public void handleRequest() throws Exception {
//some logic
} catch (Exception ex) {
throw new MyCustomException();
}
}
}
So, when the request is reaching SomeLogicClass, then some Exception(for example NPE) is thrown and then i want to throw in catch myCustomException. I expect that it will go to ControllerAdvise, but nothing and i see error 500.
Something is missing here or what? Controller and ControllerAdvise are located in some package.
I also tried to add the package
#ControllerAdvice("my.package.controller")
Found what caused that it skipped ControllerAdvise. The problem was UserPrinciple object, that is actually my custom class. Any custom class that i put additionally in input argument was a problem. I don't know the reason for this , but implemented without UserPrinciple.class.
I have a domain class defined as follows
#Data
#Entity
public class City {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long cityID;
#NotBlank(message = "City name is a required field")
private String cityName;
}
When I post to the endpoint http://localhost:8080/cities without a cityName I get a ConstraintViolationException but when I send a PUT request to the endpoint http://localhost:8080/cities/1 without a cityName I get the following exception instead of ConstraintViolationException.
{
"timestamp": 1494510208982,
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.transaction.TransactionSystemException",
"message": "Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction",
"path": "/cities/1"
}
So how do I get a ConstraintViolationException exception for a PUT request?
Note: I am using Spring Data Rest so the endpoints are generated by Spring. There is no custom rest controller.
I think Cepr0's test work for both PUT and POST, because when you send a PUT request for a non-existing entity then Spring Data Rest uses the create method in the background.
Let's assume there is no user with id=100:
calling 'PUT users/100' is the same as calling 'POST users/'
When you send PUT for an existing entity, it will generate that nasty TransactionSystemException.
I'm also fighting with the Data Rest exception-handling right now, and there are a lot of inconsistency in there.
Here is my current RestErrorAttributes class, it solves most of my problems, but there is a good chance I will fond others during the following days. :)
#Component
#Slf4j
public class RestErrorAttributes extends DefaultErrorAttributes implements MessageSourceAware {
private MessageSource messageSource;
#Override
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
/** {#inheritDoc} */
#Override
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
final Map<String, Object> errorAttributes = super.getErrorAttributes(requestAttributes, includeStackTrace);
// Translate default message by Locale
String message = errorAttributes.get("message").toString();
errorAttributes.put("message",
messageSource.getMessage(message, null, message, LocaleContextHolder.getLocale()));
// Extend default error message by field-errors
addConstraintViolationDetails(errorAttributes, requestAttributes);
return errorAttributes;
}
private void addConstraintViolationDetails(Map<String, Object> errorAttributes,
RequestAttributes requestAttributes) {
Throwable error = getError(requestAttributes);
if (error instanceof ConstraintViolationException) {
errorAttributes.put("errors",
RestFieldError.getErrors(((ConstraintViolationException) error).getConstraintViolations()));
}
else if (error instanceof RepositoryConstraintViolationException) {
errorAttributes.put("errors", RestFieldError
.getErrors(((RepositoryConstraintViolationException) error).getErrors().getAllErrors()));
}
}
/** {#inheritDoc} */
#Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
try {
Throwable cause = ex;
while (cause instanceof Exception) {
// Handle AccessDeniedException - It cannot be handled by
// ExceptionHandler
if (cause instanceof AccessDeniedException) {
response.sendError(HttpStatus.FORBIDDEN.value(), cause.getMessage());
super.resolveException(request, response, handler, (Exception) cause);
return new ModelAndView();
}
// Handle exceptions from javax validations
if (cause instanceof ConstraintViolationException) {
response.sendError(HttpStatus.UNPROCESSABLE_ENTITY.value(), "validation.error");
super.resolveException(request, response, handler, (Exception) cause);
return new ModelAndView();
}
// Handle exceptions from REST validator classes
if (cause instanceof RepositoryConstraintViolationException) {
response.sendError(HttpStatus.UNPROCESSABLE_ENTITY.value(), "validation.error");
super.resolveException(request, response, handler, (Exception) cause);
return new ModelAndView();
}
cause = ((Exception) cause).getCause();
}
} catch (final Exception handlerException) {
log.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException);
}
return super.resolveException(request, response, handler, ex);
}
#Getter
#AllArgsConstructor
public static class RestFieldError {
private String field;
private String code;
private String message;
public static List<RestFieldError> getErrors(Set<ConstraintViolation<?>> constraintViolations) {
return constraintViolations.stream().map(RestFieldError::of).collect(Collectors.toList());
}
public static List<RestFieldError> getErrors(List<ObjectError> errors) {
return errors.stream().map(RestFieldError::of).collect(Collectors.toList());
}
private static RestFieldError of(ConstraintViolation<?> constraintViolation) {
return new RestFieldError(constraintViolation.getPropertyPath().toString(),
constraintViolation.getMessageTemplate(), constraintViolation.getMessage());
}
private static RestFieldError of(ObjectError error) {
return new RestFieldError(error instanceof FieldError ? ((FieldError) error).getField() : null,
error.getCode(), error.getDefaultMessage());
}
}
}
My workaround for this was to setup an exception handler to handle the TransactionSystemException, unwrap the exception and handle like a regular ConstraintViolationException:
#ExceptionHandler(value = {TransactionSystemException.class})
public ResponseEntity handleTxException(TransactionSystemException ex) {
Throwable t = ex.getCause();
if (t.getCause() instanceof ConstraintViolationException) {
return handleConstraintViolation((ConstraintViolationException) t.getCause(), null);
} else {
return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
#ExceptionHandler({ConstraintViolationException.class})
public ResponseEntity<Object> handleConstraintViolation(ConstraintViolationException ex,
WebRequest request) {
List<String> errors = new ArrayList<>();
for (ConstraintViolation<?> violation : ex.getConstraintViolations()) {
errors.add(violation.getRootBeanClass().getName() + " " + violation.getPropertyPath() + ": " + violation.getMessage());
}
return new ResponseEntity<>(errors, new HttpHeaders(), HttpStatus.CONFLICT);
}
I'm working on a web app using Spring MVC and AngularJS, I'm creating a Rest API that returns ResponseEntities that contains JSON strings.
I want to be able when an Exception happens to return a string that contains the error cause to my view and then show this error with a modal in AngularJS, I created a Class with the #ControllerAdvice annotation and in this class I defined a method with my custom exception like this
#ControllerAdvice
public class GlobalExceptionHandlerController {
#ExceptionHandler(PersonalException.class)
public String handleCustomExceptionRazon(PersonalException ex) {
String errorMessage = "custom error";
return errorMessage;
}
}
I have the following interface
public interface ClientDAO {
public void insertCLiente(Client client) throws PersonalException
}
And in my implementation
#Override
public void insertCLiente(Client client) throws PersonalException{
//method implementation
if (searchCLiente(client.name())) {
throw new PersonalException("client aleady exists");
} else {
//method implementation
}
}
My searchClient Method
public boolean searchClient(String name) {
try {
//method implementation
} catch (DataAccessException dataAccessException) {
System.out.println("");
dataAccessException.printStackTrace();
} catch (Exception e) {
System.out.println("");
e.printStackTrace();
}
//method implementation
}
My Client Controller
#Autowired
ClientDAO clientDAO;
#RequestMapping(value = "/client/", method = RequestMethod.POST)
public ResponseEntity<Void> createClient(#RequestBody final String DTOClientData, UriComponentsBuilder ucBuilder) {
//here I parse the JSON data and create my Client object
//here I dont know how can I return the error message
clientDAO.insertClient(client);
}
My custom Exception
public class PersonalException extends Exception {
public PersonalException (String msg) {
super(msg);
}
}
I don't know un my clientController method createClient how can I return an execption of the type PersonalException that I created
//here I dont know how can I return the error message
Just throw the exception from Controller.
#RequestMapping(value = "/client/", method = RequestMethod.POST)
public ResponseEntity<Void> createClient(#RequestBody final String DTOClientData, UriComponentsBuilder ucBuilder) throws PersonalException {
You can return error message in GlobalExceptionHandlerController like this...
/**
* REST exception handlers defined at a global level for the application
**/
#ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = { PersonalException.class })
protected ResponseEntity<RestResponse> handleUnknownException(PersonalException ex, WebRequest request) {
LOGGER.error(ex.getMessage(), ex);
return new ResponseEntity<RestResponse>(new RestResponse(Boolean.FALSE, ImmutableList.of("Exception message"), null), HttpStatus.INTERNAL_SERVER_ERROR);
}
Now, you might have noticed that we are not handling the Exception even in the Controller. Instead, we are Throwing it in the declaration hoping that somewhere we have handled this exceptional case gracefully showing the User a nice looking Toaster message.
The question may remains – Where the hell i am handling the Exception? It is handling by the #ExceptionHandler in GlobalExceptionHandlerController .
I have a #ControllerAdvice class to handle exceptions from my SpringMVC controllers. I would like to catch an exception of a known type (RuntimeException) in an #ExceptionHandler method then throw the e.getCause() exception and have this exception caught by the same #ControllerAdvice class.
Sample code:
#ControllerAdvice
public class ExceptionHandlingAdvice
{
#ExceptionHandler( RuntimeException.class )
private void handleRuntimeException( final RuntimeException e, final HttpServletResponse response ) throws Throwable
{
throw e.getCause(); // Can be of many types
}
// I want any Exception1 exception thrown by the above handler to be caught in this handler
#ExceptionHandler( Exception1.class )
private void handleAnException( final Exception1 e, final HttpServletResponse response ) throws Throwable
{
// handle exception
}
}
Is this possible?
You can check if that RuntimeException is instance of Exception1.class and call the method directly:
private void handleRuntimeException( final RuntimeException e, final HttpServletResponse response ) throws Throwable
{
if (e instanceof Exception1) handleAnException(e,response);
else throw e.getCause(); // Can be of many types
}
Few years late on this.. but just ran into a need for this in dealing with #Async services - when throwing an exception, they get wrapped in the ExecutionException.class and wanted my controller advice to direct them to their proper handler, an identical situation you were in.
Using reflection, can gather all the methods on the controller advice, sift for the matching #ExceptionHandler annotation for e.getCause().getClass() then invoke the first found method.
#ControllerAdvice
public class ExceptionHandlingAdvice
{
#ExceptionHandler( RuntimeException.class )
private void handleRuntimeException( final RuntimeException e, final HttpServletResponse response )
{
if (e.getCause() != null) {
Optional<Method> method = Arrays.stream(Rest.Advice.class.getMethods())
.filter(m -> {
// Get annotation
ExceptionHandler annotation = m.getAnnotation(ExceptionHandler.class);
// Annotation exists on method and contains cause class
return annotation != null && Arrays.asList(annotation.value()).contains(e.getCause().getClass());
})
.findFirst();
if (method.isPresent()) {
try {
method.get().invoke(this, e.getCause(), response);
} catch (IllegalAccessException | InvocationTargetException ex) {
// Heard you like exceptions on your exceptions while excepting
ex.printStackTrace();
}
}
}
// Handle if not sent to another
}
... other handlers
}
Didn't test with void -- Personally, I return ResponseEntity<MyStandardErrorResponse> from my handlers, so my invoke line looks like:
return (ResponseEntity<MyStandardErrorResponse>) method.get().invoke(this, e.getCause(), request);