I have a basic SpringBoot 2.0.4.RELEASE app. Using Spring Initializer, JPA, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR file.
I have created this class to manage the exceptions
#ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
public RestResponseEntityExceptionHandler() {
super();
}
// API
// 400
...
}
But when I try to use it in 1 of my method :
#GetMapping(path = "/getUsers", consumes = "application/json", produces = "application/json")
#ExceptionHandler({RestResponseEntityExceptionHandler.class })
public ResponseEntity<List<User>> testErrors(HttpServletRequest request, #RequestHeader(value = "Authorization") String authHeader) {
...
}
I got this compilation error :
Type mismatch: cannot convert from Class to Class>
If you read the docs for #ExceptionHandler you will see that it is used to mark a method to handle exception(s). Therefore you have to specify which exception(s) it can handle (e.g. #ExceptionHandler(MyException.class) or #ExceptionHandler({MyException.class, MyOtherException.class})). When such an exception occurs in your controller this method gets invoked.
#ControllerAdvice makes the methods defined in the class available to all your controllers.
I don't know what you want to do, but since you extend ResponseEntityExceptionHandler in the #ControllerAdvice class, you could just delete the #ExceptionHandler annotation:
#GetMapping(...)
public ResponseEntity<List<User>> testErrors(...) {
...
}
If you want to handle specific exceptions on your own, you can do it like this:
#ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(MyException.class)
public ResponseEntity<?> handleMyException() {
...
}
}
#Controller
public class MyController {
#GetMapping(...)
public ResponseEntity<List<User>> testErrors(...) {
throw new MyException();
}
}
Now handleException() will be called when an error occurs in testErrors().
If you want to handle exceptions only in one controller you can do this:
#Controller
public class MyController {
#GetMapping(...)
public ResponseEntity<List<User>> testErrors(...) {
throw new MyException();
}
#ExceptionHandler(MyException.class)
public ResponseEntity<?> handleMyException() {
...
}
}
It's expected for this not to work correctly.
You're attempting to pass in the RestResponseEntityExceptionHandler as the arguments of the #ExceptionHandler annotation in your test method. This is wrong, as this annotation accepts the type of the exception that gets intercepted.
Also in general it seems that the placement of your #ExceptionHandler annotation seems to be wrong. This are placed within method that reside in the actual exception handling class.
I suggest you have a good read on the way Spring handles exceptions in that manner.
Have a look at this piece of documentation: exception handling in Spring.
Related
I have my Spring error controller, and I need to get the actual exception class so I can print stack trace and other things like that.
This is my error controller
#Controller
public class ErrorController implements org.springframework.boot.web.servlet.error.ErrorController {
#RequestMapping("/error")
public String handleError() {
return "somethingwentwrong";
}
#Override
public String getErrorPath() {
return null;
}
}
I know its not much, but I need the exception object to be able to do some extra handling.
If you wish to execute different code based on the TYPE of the exception thrown, you should look at using #ControllerAdvice along with #ExceptionHandler. Any exceptions that you do not handle with an #ExceptionHandler will then bubble up to the default ErrorController (though you could handle Exception in a handler and then all exceptions will be handled via your custom handler). Something like:
#ControllerAdvice
#RestController
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(MyException.class)
protected ResponseEntity<String> handleMyException(MyException ex) {
// Your code here
}
#ExceptionHandler(Exception.class)
protected ResponseEntity<String> handleException(Exception ex){
// Your code here
}
}
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-exceptionhandler
No worries guys, I solved the problem, I just had to do some digging into springs built in error controller to get the stacktrace.
A sample project demonstrating the issue can be found at: https://github.com/dirkbolte/javaexceptionhandlertest
I have a controller which is declared to produce JSON content:
#RestController
public class TestController {
#GetMapping(value = "exception", produces = MediaType.APPLICATION_JSON_VALUE)
public void test() {
}
}
The corresponding controller advice should map any exception to a BAD_REQUEST :
#RestControllerAdvice
public class MyExceptionHandler {
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(value = Exception.class)
public void exceptionhandler() {
}
}
I test that a call with a wrong accept header results in the correct error response code (as procudes specifies something different):
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureMockMvc
class TestControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void test() throws Exception {
mockMvc.perform(
MockMvcRequestBuilders.get("/exception")
.accept(MediaType.APPLICATION_PDF)
).andExpect(status().isBadRequest());
}
}
The code above "fails successfully" :-) . In my production code I want to limit the controller advice to a certain controller (alternatively to a certain package), thus I specified assignableTypes on #ControllerAdvice: #RestControllerAdvice(assignableTypes = TestController.class) . Now the test fails as the exception handler isn't called anymore. Same happens when specifying basePackages or basePackageClasses - or when using an interface.
The version on GitHub shows the test failure, so I hope it is easy to reproduce.
How can I use a ControllerAdvice explicitly for one or more Controller?
The requested URL will not get mapped to any Controller method here. This would result in a Status 406 error (due to the specified accept type). Since no exception is thrown from the Controller methods the ControllerAdvice would not be invoked.
On dispatcher level, org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#getExceptionHandlerMethod will be called with handlerMethod = null which is why only generic controller advices are applied.
The ControllerAdvice would work here, if the request contains additional metadata that the controller method is configured for and/or an exception is thrown from within that method.
I would like to manage Exception thrown by simple Controller or RestController in two ways:
1) html redirection
2) Json error
I tested the code below :
#ControllerAdvice(annotations = Controller.class)
public class ExceptionHandlerController
{
#ExceptionHandler(Exception.class)
public ModelAndView handleException(HttpServletRequest _req, Exception _ex)
{
K_LOGGER.info("test");
return new ModelAndView();
}
}
#ControllerAdvice(annotations = RestController.class)
public class ExceptionHandlerRestController
{
#ExceptionHandler(Exception.class)
public ResponseEntity<String> handleException(HttpServletRequest _req, Exception _ex)
{
return new ResponseEntity<>("test", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
#RestController
public class GreetingController
{
#RequestMapping("/greetingexception")
public Greeting greetingException(#RequestParam(value = "name", defaultValue = "World") String name)
throws Exception
{
throw new Exception();
}
}
It doesn't work properly, I always pass by ExceptionHandlerController but not by ExceptionHandlerRestController.
I think it's because #RestController inherit of #Controller.
Do you have a other solution?
Try to add #Order(Ordered.HIGHEST_PRECEDENCE) annotation to rest exception handler. It may helps you.
eg04lt3r answer is correct, just though that more details might be useful for someone.
In case when you have global #ControllerAdvice and want to handle some exception in a different way in one of your Controllers you need to set #Order(Ordered.HIGHEST_PRECEDENCE) on the #ControllerAdvice which should have higher priority.
For example:
#ControllerAdvice
public class GeneralExceptionHandler {
#ExceptionHandler(Exception.class)
protected ResponseEntity<Error> handleException(Exception ex) {
...
}
}
#ControllerAdvice(assignableTypes = MyController.class)
#Order(Ordered.HIGHEST_PRECEDENCE)
public class MyExceptionHandler {
#ExceptionHandler(Exception.class)
protected ResponseEntity<Error> handleException(Exception ex) {
...
}
}
#Order is needed because on startup one of the handlers will register with higher order automatically, anyway and your exception handling will become unpredictable. For example I recently saw a case when if you start an app using bootRun gradle task MyExceptionHandler was primary, but when started as jar GeneralExceptionHandler was primary.
Here,my requirement is that i want separate code in my application for exception handling,i saw a nice option of spring there using #controller advice to handle exceptions globally.
#ControllerAdvice
class GlobalControllerExceptionHandler {
#ResponseStatus(HttpStatus.CONFLICT) // 409
#ExceptionHandler(DataIntegrityViolationException.class)
public void handleConflict() {
// Nothing to do
}
}
But there i want to cutomization there,like proper dynamic messages,own error code. so how can i do this,i am new to spring boot and even i don't have knowledge of spring.Need basic example.
You can come up with a class like this to capture information to be sent in response in case of exception:-
public class APIResponse {
int errorCode;
String description;
String someInformation;
// any other information that you want to send back in case of exception.
}
#ControllerAdvice
class GlobalControllerExceptionHandler {
#ResponseStatus(HttpStatus.CONFLICT) // 409
#ResponseBody
#ExceptionHandler(DataIntegrityViolationException.class)
public APIResponse handleConflict(DataIntegrityViolationException exception) {
APIResponse response = createResponseFromException(exception);
return response;
}
}
In your controller advice class:-
Have the return type APIResponse instead of void.
The handler method can have the exception raised as the argument.
Using the exception object to create the APIResponse object.
Put #ResponseBody on the handler method.
I'm developing a REST service using Spring MVC. I'm trying to implement exception handling using #ExceptionHandler . When exception is thrown from REST layer, it's not been intercepted by #ExceptionHandler. Am i missing anything ?
#Service
#Path("/customer")
public class CustomerResource extends BaseResource{
#Autowired
private CustomerDao customerDao;
........
#GET
#Path("/customer/{accountNumber}")
public Response findCustomerByAccountNumber(String accountNumber) throw Exception{
Customer customer=null;
customer=customerDao.find(....);
if(customer==null)
throw new ResourceNotFoundException();
else
..........
}
}
Base class which has Exception handler method
public abstract class BaseResource {
.......
#ExceptionHandler({ResourceNotFoundException.class })
public Response handleException(Exception ex) {
ErrorResource errResource = new ErrorResource();
.....
return Response.status(Response.Status.NOT_FOUND).entity(errResource).build();
}
}
You are throwing ResourceNotFound but have specified ResourceNotFoundException in the exception handler - these seem to be different exceptions. Either throw ResourceNotFoundException instead of ResourceNotFound or add ResourceNotFound to the exception handler.
EDIT:
Don't know how I missed it at first: just noticed you don't actually use Spring MVC controller. Spring MVC exception handlers only work for requests handled by Spring MVC controllers. They handle exceptions that happen in the body of controller handler methods. You seem to use something else to handle REST requests.