I have the following service class:
#Service
public class CitiesServiceImpl implements CitiesService {
#Autowired
private CitiesRepository citiesRepository;
#Override
public City getCityById(Integer cityId) {
return citiesRepository.findById(cityId)
.orElseThrow(ResourceNotFoundException::new);
}
}
and it is used in my controller:
#RestController
#RequestMapping("/cities")
public class CitiesController {
#Autowired
private CitiesService citiesService;
#GetMapping("/{cityId}")
public City readCity(#PathVariable Integer cityId) {
return citiesService.getCityById(cityId);
}
#ExceptionHandler(ResourceNotFoundException.class)
String handleResourceNotFound(Exception e) {
return e.getMessage();
}
}
So when the readCity is called with a nonexistent cityID, the ResourceNotFoundException will be thrown and then handled by the handleResourceNotFound exception handler.
However, when the ResouceNotFoundException is handled, the status code in the response is still 202, i.e. OK. Seems that #ResponseStatus annotation in the ResourceNotFoundException is not utilized at run time. This can be fixed by adding the #ResponseStatus(value=HttpStatus.NOT_FOUND) to the method handleResourceNotFound, but such code is duplicate as the #ResponseStatus annotation is already in the ResourceNotFoundException.
Question: How to utilize the ResponseStatus annotation of the ResourceNotFoundException instead of adding duplicate code?
Remove that handleResourceNotFound and let framework handle that for you, or return proper Response from handleResourceNotFound method.
By declaring such handler you are saying that you will handle such case, thus framework is backing off.
Related
I've developed a rest api using Spring Boot. In one of my service methods, I throw a ServletException, in case a specific user is not found. I am wondering if that is the best way to do that, I mean, is that the right layer to thrown the exception?
Creating a custom exception type is a better idea than using ServletException.
In order to handle an exception you can use #ControllerAdvice.
First create custom exception type:
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
Assuming that your controller and service look more or less like this:
#RestController
#RequestMapping("users")
class UserController {
private final UserService userService;
UserController(UserService userService) {
this.userService = userService;
}
#GetMapping
List<String> users() {
return userService.getUsers();
}
}
#Service
class UserService {
List<String> getUsers() {
// ...
throw new UserNotFoundException("User not found");
}
}
You can handle you UserNotFoundException using #ControllerAdvice
#ControllerAdvice
class CustomExceptionHandler {
#ExceptionHandler({UserNotFoundException.class})
public ResponseEntity<Object> handleUserNotFoundException(UserNotFoundException exception) {
return new ResponseEntity<>(exception.getMessage(), HttpStatus.NOT_FOUND);
}
}
One of best way or what I do is,
Check data / parameters for valid data( e.g Null check sometime manually using if statement).
Checking data / parameters for size (like file size)
checking data or parameters for valid range also data type, do typecasting if not in valid type (like String, Long, Integer etc).
Raise a message and return to that API, if the data / parameters are not valid before system raise an exception
I am assuming you are looking to catch all exceptions occured inside your application. Spring-Boot provides a Global Exception Handler to catch all the exceptions gracefully and return response according to the the specific Exception.
It gives you flexibility to change the status codes, response data, headers accordingly. Few useful links to implement this feature is -
1.) Dzone
2.) Spring Boot Tutorial
Throwing exception in your #Service is okay. ServletException is not super meaningful. What I would suggest is to create your own Exception class extending RuntimeException and throw it.
So you would end up with something like that:
A Controller that only calls a service method (better not to have any
logic here)
#RestController
#RequestMapping("/users")
public class UserController {
#Autowired
private UserService userService;
#GetMapping("/{id}")
public User getUserById(#PathVariable("id") Long id) {
return userService.getById(id);
}
}
A Service class that calls DAO class (extending JPARepository)
#Service
public class UserServiceImpl implements UserService {
#Autowired
private UserDAO userDAO;
#Override
public User getById(Long id) {
return userDAO.findById(id).orElseThrow(() -> new UserNotFoundException("No user with id = " + id + " found."));
}
}
DAO:
#Repository
public interface UserDAO extends JpaRepository<User, Long> {
}
note: it returns Optional<Object> which is very convinient.
And finally your own Exception class.
#ResponseStatus(HttpStatus.NOT_FOUND)
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
Note: #ResponseStatus - it is going to return HTTP Status code 404 on throwing this exception.
This is imho a very clean and good way to develop your rest api.
Also take a look here: How to get spesific error instead of Internal Service Error . I answered a question providing information you might find useful
I'm writing a simple library API for a college project. I have a database with books, each with it's own ID. I'm using Spring Boot to make the service. I have a BookRepository which extends JpaRepository<Book, Long>, and a service implementation.
#Service
public class BookServiceImpl implements BookService{
#Autowired
private BookRepository bookRepository;
#Async
#Override
public void delete (Long id){
bookRepository.delete(id);
}
}
Later on, a REST controller handles the request:
#RestController
public class BookServiceController{
#Autowired
private BookService bookService;
#RequestMapping(value="books/{id}", method = RequestMethod.DELETE)
public ResponseEntity<Book> deleteBook (#PathVariable("id") Long id){
bookService.delete(id);
return new ResponseEntity<Book>(HttpStatus.OK);
}
}
Now, if I were to delete a Book which is not in the database, for example with an ID of 123, I'd get a EmptyResultDataAccessException thrown.
My question is, how and where do I handle the exception, and how do I avoid casting a NullPointerException that way?
Thanks in advance.
In the case of a DELETE operation, you aren't really returning an entity anymore; you're simply confirming that the resource is gone. As DELETE is idempotent (you could delete the record multiple times), you can either return the same status code regardless of whether the record exists or return a 404 if the record isn't found. You can also simplify the handler method:
#DeleteMapping("/books/{id}")
#ResponseStatus(HttpStatus.NO_CONTENT) // because you deleted it
public void deleteBook(#PathVariable Long id) {
try {
bookService.delete(id); // or just use the repository directly
} catch (EmptyResultDataAccessException ex) {
// either do nothing to return a 204, or
throw new NotFoundException();
}
}
where you have an exception that indicates the status:
#ResponseStatus(HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException {}
It's plausible that EmptyResultDataAccessException should already be annotated with a status of NOT_FOUND; this is a potential enhancement request.
The most elegant way to avoid the EmptyResultDataAccessException is to define an specific method in your Repository interface to delete by your id field.
Let's supppose that your id field is named bookId:
public interface BookRepository extends JpaRepository<Book, Long> {
Long deleteByBookId(Long bookId);
}
In this way the exception won't be throwed.
I haven't tested the solution if your id field is simply called id.
I use #ControllerAdvice to handle all my app exceptions :
#ControllerAdvice
public class ExceptionHandlingController {
#ExceptionHandler({UnauthorizedException.class})
public String unauthorizedException() {
.........
}
#ExceptionHandler({UnauthorizedAjaxException.class})
#ResponseBody
public void unauthorizedAjaxException() {
.........
}
#ExceptionHandler({Exception.class})
public String globalException(){
.........
}
}
And somewhere in my code i do throw new UnauthorizedException();
#Around("#annotation(Authenticated)")
public Object profilingAuthentication(ProceedingJoinPoint pjp) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
if( request.getSession().getAttribute("idContact") == null ) {
if( "XMLHttpRequest".equals(request.getHeader("X-Requested-With")) )
throw new UnauthorizedAjaxException();
throw new UnauthorizedException();
}
return pjp.proceed();
}
But sadly Spring MVC appears to be acting random by using the most generic case (Exception) rather than more specific ones (UnauthorizedException for example). And sometimes he choose the correct one !
How the order works works ? and is there any way to specify the order ?
UnauthorizedException is a custom exception
public class UnauthorizedException extends Exception {
public UnauthorizedException(){
super();
}
public UnauthorizedException(String message){
super(message);
}
}
UPDATE
i found out that the order it's not rondom actually the methods who throw UnauthorizedException works normally but the others not !
#Authenticated
#RequestMapping(value="/favoris")
public String favoris(ModelMap model, HttpServletRequest request)
throws UnauthorizedException {
....
}
#Authenticated
#RequestMapping(value="/follow")
public String follow(ModelMap model, HttpServletRequest request) {
.....
}
So i have to add throws UnauthorizedException manually or there is some other solution ?
we are using exception handler in following way and never order get mixed and it work as expected.
So it could be possible if you will use it as following example then it will solve your problems
**********handler class******************
#ControllerAdvice
public class GlobalExceptionHandler {
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(value = Exception.class)
public boolean handle1(Exception exc) {
System.out.println("#####Global Exception###" + exc);
exc.printStackTrace(System.out);
return true;
}
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(value = CustomException.class)
public boolean handle2(CustomException exc) {
System.out.println("###custom exception######" + exc);
exc.printStackTrace(System.out);
return true;
}
}
***************Controller class************
#RestController("test")
#RequestMapping("/test1")
public class TestController {
#RequestMapping("/t1")
public boolean test() {
if (true) {
throw new CustomException();
}
return true;
}
}
In above example exception habdler is handle2 because 1st of all it will search for matching exception if not found then go for parrent handler
If we throw new NullPointerException() then it will search for matching handler but not found in this case then go for parrent that is handle1
for more you can refer here
I hope it will help you. Thanks
Use an annotation #Order or implement an interface Ordered for #ControllerAdvice.
See implementations of:
ExceptionHandlerMethodResolver class
ExceptionHandlerExceptionResolver class (method initExceptionHandlerAdviceCache)
AnnotationAwareOrderComparator class
There is no order/priority as long as you have a single controlleradvice class in your project. But if you have multiple controlleradvice classes, you can set the Order. But here, in your case, the order is not applicable as the two exceptions are handled differently (i.e., UnauthorizedException and Exception).
The good thing is, Spring will automatically find the respective custom Exception class (if any,
otherwise generic Exception) and invoke the corresponding method.
Please refer for more information on Spring Controller Advice and Exception handling:
https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
I have a exception class like follows
#ResponseStatus(value=HttpStatus.UNPROCESSABLE_ENTITY, reason="Unprocessable Entity") // 422
public class UnprocessableEntityException extends RuntimeException {
}
Now the status is not returned as 422 unless I write a specific handler in the Controller class like :
#ExceptionHandler(UnprocessableEntityException.class)
#ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
public String handleException(Exception ex) {
...
}
As I understand I should not need #ExceptionHandler in first place, not sure what am I missing.
Throwing a #ResponseStatus annotated exception from a controller method should be enough for the framework to write the HTTP status code - no #ExceptionHandler necessary.
The following will write a 422 Status on hitting the webapp root as expected:
#Controller
public class ExceptionController {
#RequestMapping("/")
public void action() {
throw new ActionException();
}
#ResponseStatus(value = HttpStatus.UNPROCESSABLE_ENTITY, reason = "nope")
public static class ActionException extends RuntimeException {}
}
This works courtesy of the ResponseStatusExceptionResolver which is created by Spring MVC by default - if it's not working for you, my guess is that this default exception resolver has been removed (by e.g. overriding WebMvcConfigurationSupport.configureHandlerExceptionResolvers or otherwise configuring your context's HandlerExceptionResolvers so that the ResponseStatusExceptionResolver is trumped.)
The exception thrown should not be handled by code or by other exception resolvers, for example it shouldn't be handled by #ExceptionHandler, because that will override the status code specified by the exception class's #ResponseStatus.
Good day!
I have a #Controller. Some of its methods throw the same exception, but I want to handle this exceptions in different way.
Is there a way how to bind an #ExceptionHandler to a particular method?
You need to use AOP tools like CDI Interceptor or AspectJ to achieve this cross-cutting concerns. A Concern is a term that refers to a part of the system divided on the basis of the functionality.
Basically this type of feature is used to handle logging, security and also handling the errors... which are not part of your business logic...
Like if you want to change the logger for application from log4j to sl4j then you need to go through each and every classes where you have used log4j and change it. But if you have used AOP tools then you only need to go the interceptor class and change the implementation. Something like plug and play and very powerful tool.
Here is a code snippet using JavaEE CDI Interceptor
/*
Creating the interceptor binding
*/
#InterceptorBinding
#Retention(RUNTIME)
#Target({TYPE, METHOD})
public #interface BindException {
}
After we have define interceptor binding we need to define interceptor binding implementation
/*
Creating the interceptor implementation
*/
#Interceptor
#BindException
public class ExceptionCDIInterceptor {
#AroundInvoke
public Object methodInterceptor(InvocationContext ctx) throws Exception {
System.out.println("Invoked method " + ctx.getMethod().getName());
try {
return ctx.proceed(); // this line will try to execute your method
// and if the method throw the exception it will be caught
} catch (Exception ex) {
// here you can check for your expected exception
// code for Exception handler
}
}
}
Now we only need to apply interceptor to our method
/*
Some Service class where you want to implement the interceptor
*/
#ApplicationScoped
public class Service {
// adding annotation to thisMethodIsBound method to intercept
#BindException
public String thisMethodIsBound(String uid) {
// codes....
// if this block throw some exception then it will be handled by try catch block
// from ExceptionCDIInterceptor
}
}
You can achieve same feature using AspectJ also.
/*
Creating the Aspect implementation
*/
#Aspect
public class ExceptionAspectInterceptor {
#Around("execution(* com.package.name.SomeService.thisMethodIsBound.*(..))")
public Object methodInterceptor(ProceedingJoinPoint ctx) throws Throwable {
System.out.println("Invoked method " + ctx.getSignature().getName());
try {
return ctx.proceed(); // this line will try to execute your method
// and if the method throw the exception it will be caught
} catch (Exception ex) {
// here you can check for your expected exception
// codes for Exception handler
}
}
}
Now we only need to enable the AspectJ to our application config
/*
Enable the AspectJ in your application
*/
#Configuration
#EnableAspectJAutoProxy
public class AppConfig {
#Bean
public SomeService SomeService() {
return new SomeService();
}
}
/*
Some Service class where you want to implement the Aspect
*/
package com.package.name;
public class SomeService {
public String thisMethodIsBound(String uid) {
// codes....
// if this block throw some exception then it will be handled by try catch block
// from ExceptionAspectInterceptor
}
}
I have code example in my git repo https://github.com/prameshbhattarai/javaee-exceptionBinding by using CDI interceptor.
Just as an option (obviously, it's not ideal): you can wrap the exception into a custom exception in one of your methods and then catch it in an #ExceptionHandler
void boo() throws WrappingException {
try {
} catch (TargetException e) {
throw new WrappingException(e);
}
}
then
#ExceptionHandler(WrappingException.class)
public void handleWrappingException() {
// handle
}
#ExceptionHandler(TargetException.class)
public void handleTargetException() {
// handle
}
Could you please explain why do you need this? I'm asking out of curiosity, because I've never felt like this is required and here is why:
Exception usually represents a very specific "mistake" - something that went wrong in a very specific way.
Basically, exception represents a mistake, not a flow...
There are two "degrees of freedom" that spring can support out of the box:
Exception parameters. Maybe stuff like error code, which can be declared as a data field of exception itself.
Exception inheritance. Example:
If you have in your system a UserDoesNotExistException and you want to be more specific in a case of say, the system that manages the users that are retired in some flows, you can always create a more specific exception:
class UserRetiredException extends UserDoesNotExistException {...}
Obviously, spring can support both the cases: In ExceptionMapper you have the access to the exception anyway so you can do something like:
handleException(SomeExceptionWithErrorCode ex) {
if(ex.getErrorCode() == "A") {
// do this
}
else if(ex.getErrroCode() == "B") {
// do that
}
}
In the second case you just have different exception mappers for the different types of exceptions.
You can also consider #ControllerAdvice annotation to reuse code or something.
I don't think you can specify a specific #ExceptionHandler for a method, but you can bind an #ExceptionHandler method to a specific Exception.
So if you want to handle all DataIntegrityViolationException one way and all other Exceptions in another you should be able to achieve that with something like this:
#ExceptionHandler(DataIntegrityViolationException.class)
public void handleIntegrityViolation() {
// do stuff for integrity violation here
}
#ExceptionHandler(Exception.class)
public void handleEverythingElse() {
// do stuff for everything else here
}
You can derive sub-exceptions from the common exception thrown by other methods according to how you want to handle them.
Say you have declared the parent exception as ParentException. Derive sub classes like ChildAException extends ParentException, ChildBException extends ParentException etc.
Define a #ControllerAdvice class that catches the ParentException and define the specific behaviors in delegate methods.
#ControllerAdvice
public class ParentExceptionHandler {
#ExceptionHandler(ParentException.class)
public ResponseEntity<Object> handleParentException(ParentException pe) {
if (pe instanceof ChildAException) {
return handleChildAException((ChildAException) pe);
} else if (...) {
...
} else {
// handle parent exception
}
}
private ResponseEntity<Object> handleChildAException(ChildAException cae) {
// handle child A exception
}
}
I just got the same issue like you. So I checked the spring source code for this situation. It seems that spring will search in the #Controller class for any method that is annotated with #ExceptionHandler first, if nothing matched then it will continue to search for all class that is annotated with #ControllerAdvice. So you can just use the strategy below:
MyController with a #ExceptionHandler method:
#RestController
public class MyController {
#RequestMapping("/foo")
public String foo() {
throw new IllegalArgumentException();
}
#ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handle(IllegalArgumentException ex) {
return new ResponseEntity<>("Specific handler", HttpStatus.BAD_REQUEST);
}
}
AnotherController without any method annotated with #ExceptionHandler:
#RestController
public class AnotherController {
#RequestMapping("/bar")
public String bar() {
throw new IllegalArgumentException();
}
}
A global #ControllerAdvice class:
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handle(IllegalArgumentException ex) {
return new ResponseEntity<>("Global handler", HttpStatus.BAD_REQUEST);
}
}
Then if you visiting http://ip:port/foo, you will get 400 status code with Specific handler, and 400 status code with Global handler when you visit http://ip:port/bar.
I agree that the inability to map a specific #ExceptionHandler to handle only one specific method in the #RestController should be a very desirable feature.
I tried try{}catch(Exception ex){} and catch no exception. But
exception handler handles it nicely.
Since we are talking about hibernate exceptions, these exceptions are usually thrown at the commit phase of transaction. The problem here is that seems like you have transaction opened right in your controller which is considered as a bad practice.
What you should do is - open transaction in the application layer.
Controller just maps xml/json to incomming RequestDto object.
And then you call the Service to handle the business logic.
The Service(or its method) should be annotated by #Transactional.
#RestController
public class MyController {
#Autowired // but better to use constructor injection
private MyService myService;
public ResponseDto doSomething(RequestDto request) {
try {
myService.doSomething(request);
} catch (DataIntegrityViolationException ex) {
// process exception
}
}
}
#Transactional
class MyService {
public void doSomething() {
// do your processing which uses jpa/hibernate under the hood
}
}
Once you done that, the try catch will start behaving as expected on controller level.
However, I would even go further as DatabaseExeption shouldn't really go that far to controller. The alternative would be to use manual transaction inside of a service and do a try catch there.
Then in the Service layer transform database exception in a more generic exception with all necessary information for controllers to process.
And then you should catch that more generic exception (MyDatabaseAccessException) in the controller and transform as you wish for the sake of a presentation layer.
===
The #ControllerAdvice suggested here is good for a global exception handling across controllers.
The #ExceptionHandler is not suitable for each method unless you wnat to have controller per method. And even after that it can clash with global #ControllerAdvice.
I am not sure why spring doesn't allow #ExceptionHandler at a method level, it would simplify a lot of cases like yours.
My solution is to annotate a method with a marker:
#ExceptionHandler(SomeException.class)
public ResponseEntity<String> handleSomeException(SomeException e, HandlerMethod handlerMethod) {
var marker = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getMethod(), MarkerAnnotation.class);
if (marker != null) return something();
else return somethingElse();
}