I am building a REST API with Spring boot and DAO layer is implemented in Hibernate.I need to understand the correct way of throwing and handling Exception in the Application.Currently I am doing it in this way
#Repository
public class UserDaoImpl
{
public getAllUsers() throws Exception
{
//get All Users from DB
}
}
#Service
public class UserServiceImpl
{
public getAllUsers throws MyCustomException
{ try{
userDaoImpl.getAllUsers();
}
catch(Exception e)
{
throw MyCustomException();
}
}
}
and In Exception Mapper
#ControllerAdvice
public class ApplicationExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler({MyCustomException.class})
#ResponseBody
public ResponseEntity<?> handleCustomException(Exception e) {
log.error("", e);
Map<String, String> error = new HashMap<String, String>();
error.put("message", e.getMessage());
return new ResponseEntity<>(error, HttpStatus.NOT_ACCEPTABLE, MessageResource.getLogMessage("BAD_REQUEST_EXCEPTION"));
}
}
public class MyCustomException extends RuntimeException
{
///// ....
}
So I have added throws clause (throws Exception) in DAO layer and catch at service layer and wrap it in Custom Exception(unchecked exception) and do not propogate the exception at controller layer.
Is this correct ? or there is some better way?
I'd recommend you to have general #ExceptionHandler({Exception.class}) for all cases that you don't want to handle specifically.
Also it's okay to create separate exception classes for situations that require custom handling.
It depends on what do you want to achieve.
About your case. Exception in DAO layer does not necessary mean that request was wrong or did not provide correct parameters. It could be mapping problems, DB access problems and etc. So I would not wrap it to my custom exception, or at lest wrap in to general DataAccessException, make good logging around that and return some general error code to the client.
Related
Is there a way to intercept DataAccessException that is thrown by data-layer (#Repository) with knowing which method is causing this exception?
Writing custom SQLExceptionTranslator does not fit my need as I cannot determine which method caused the exception.
I have a repository like this:
public interface UserRepository extends JpaRepository<UserEntity, Integer> {
#ErrorCode("E1000")
User findById(int id);
#ErrorCode("E1001")
User findByUsername(String username);
}
ErrorCode is a custom annotation holds an error code that I need to send to client whenever DataAccessException occurs.
If there is a way to intercept the call to findById with catching DataAccessException, then it is easy to extract error code from annotation and re-throw a custom exception that can be catched by exception handler.
If Spring AOP is allowed, you could build your own aspect, for example:
#Aspect
public class ErrorCodeAspect {
#Around("#annotation(errorCode)")
public Object aroundErrorCode(ProceedingJoinPoint joinPoint, ErrorCode errorCode) throws Throwable {
try {
return joinPoint.proceed();
} catch (DataAccessException dae) {
throw new YourCustomException(errorCode.value(), dae);
}
}
}
Note that annotations on interface methods are not inherited by implementing class methods (even with #Inherited which applies to parent classes only), so you will probably need to annotate your concrete service classes instead for the aspect to plug in (unless Spring does some additional black magic with the repository proxies I wouldn't be aware of).
You can define the custom ExceptionHandler.
#RestControllerAdvice
public class RestExceptionResolver {
#ExceptionHandler(DataAccessException.class)
public ResponseEntity<String> handleNoSuchElementException(DataAccessException ex) {
return ResponseEntity.status(yourErrorCode);
}
}
So I've been using Spring and Java for a while to build microservices. I am concerned by the way I am currently handling service layer results which uses "business exception"
Controller
#RestController
public class PurchaseController {
#Autowired
private PurchaseService purchaseService;
#PostMapping("/checkout")
public ResponseEntity<?> checkout(#RequestBody CheckoutRequest body) {
try {
SomeDTO dto = purchaseService.doCheckout(body);
return ResponseEntity.ok(dto);
}
catch (UnauthorizedException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage());
}
catch (CustomBusinessException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}
}
Service
#Service
public class PurchaseService {
// ...
public DTO doCheckout(CheckoutRequest request) {
// this one calls another microservice
if (!isUserValid(request.userId)) {
// current handling of business rules violation (1)
throw new UnauthorizedException("User not valid");
}
if (request.total < 10) {
// current handling of business rules violation (2)
throw new CustomBusinessException("Minimum checkout at 20 dollars");
}
// ... do actual checkout
return new DTO(someDTOData);
}
}
I was comfortable at using this "pattern" because I do not need to "if" the business result in the controller level to return the appropriate HttpStatusCode, but since I've found some articles saying that exception is expensive specifically in Java, I doubt what I was doing is good for the long run.
Is there another correct way to gracefully handles the business result layer?
The problem with ResponseEntity in Spring is that they are typed with the result object you want to return when the endpoint is called successfully, so you can't return another body different from the happy path one, that in your case would be SameDTO. One way to address this issue is to use ? as the type of the response entity, as you have done but it is not the most recommended way.
So the best way to do this is precisely to use exceptions when there is a situation when you can't return the expected object and you have to return another object or status code, but instead of using a try-catch in the controller you should use an exception handler (Controller Advice) https://www.baeldung.com/exception-handling-for-rest-with-spring.
This controller advice would catch any exception thrown in your application and depending on the exception type it could return a different response class or status code without affecting the main controller. One example of how can be your controller advice would be:
#ControllerAdvice
public class ErrorHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(RuntimeException.class)
public ResponseEntity<String> handleInternal(final RuntimeException ex) {
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ex.getMessage());
}
#ExceptionHandler(UnauthorizedException.class)
public ResponseEntity<ResponseDto> identityClientException(UnauthorizedException e) {
return ResponseEntity
.status(HttpStatus.UNAUTHORIZED)
.body(e.getMessage());
}
#ExceptionHandler(CustomBusinessException.class)
public ResponseEntity<ResponseDto> identityClientException(CustomBusinessException e) {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(e.getMessage());
}
And your controller woulb be much more clean without exception handling logic:
#RestController
public class PurchaseController {
#Autowired
private PurchaseService purchaseService;
#PostMapping("/checkout")
public ResponseEntity<SomeDTO> checkout(#RequestBody CheckoutRequest body){
SomeDTO dto = purchaseService.doCheckout(body);
return ResponseEntity.ok(dto);
}
}
I'm trying to build a small REST service using Quarkus. I'm using Hibernate and a PostgreSQL database. It works pretty well in all good cases. But when there are Hibernate exceptions like ConstraintViolationException I'm not able to catch them in a normal way. The exceptions are wrapped with to other exception ArcUndeclaredThrowableException and RollbackException. So the exceptions can just be catched by using
catch (ArcUndeclaredThrowableException e) {
...
}
Repository
#Dependent
public class UserRepository {
#Transactional
public void createUser(User user) {
getEntityManager().persist(user); //<- the constraint violation happens at commit, so when transaction will be closed
}
}
Resource
#Override
public Response createUser(#Valid CreateUserDTO createUserDTO, UriInfo uriInfo) {
...
try {
userRepository.createUser(user);
} catch (ArcUndeclaredThrowableException e) { //<- here the hibernate exception should be catchable
log.error(e.getMessage());
throw e;
}
return Response.ok().build();
}
Because of this issue it's also not possible to add an ExceptionMapper for HibernateExceptions.
Does anybody had similar problems or is there a general problem with my code? I'm using Java11.
I would do it this way :
try {
getEntityManager().persist(user);
getEntityManager().flush();
} catch(ConstraintViolationException e) {
throw new MyCustomException(e);
}
And create Exception mapper for MyCustomException.
You can flush the Hibernate session this should triggers exceptions like ConstraintViolationException without commiting the transaction.
In your case this should be something like
#Dependent
public class UserRepository {
#Transactional
public void createUser(User user) {
getEntityManager().persist(user);
getEntityManager().flush();// should triger ConstraintViolationException
}
}
I had the same problem today, and found a workaround.
The problem, as far as I can understand is, that Arc (the cdi implemtation of quarkus) sometimes needs to generate classes.
Checked exceptions (like javax.transaction.RollbackExcpetion) need to be somehow propegated to the user. The checked Exception is therefore wrapped inside the ArcUndeclaredThrowableException. This only needs to be done however if you do not explicitly handle the exception.
For example, you can just declare the exception:
#Dependent
public class UserRepository {
#Transactional
public void createUser(User user) throws RollbackException{
getEntityManager().persist(user);
}
}
In your Resource, you can then catch the RollbackException
#Override
public Response createUser(#Valid CreateUserDTO createUserDTO, UriInfo uriInfo) {
...
try {
userRepository.createUser(user);
} catch (RollbackException e) {
log.error(e.getMessage());
throw e;
}
return Response.ok().build();
}
I am writing one REST api. There might be two exceptions in my DAO layer namely Exception_X and Exception_Y. If I encountered a exception Exception_X in DAO layer, my controller should return status code 200, if Exception_Y then 401 and if all goes well controller should return 201.
Now what was I thinking that I will throw encountered exception as it is from DAO layer to controller via service layer and in catch block of controller I will return response.
Is it acceptable or there is some other standard way?
Yes that is quite an acceptable way. However, rather than using try-catch, I would suggest to implement Exception Handlers for your REST Controllers. That way, you won't have to clutter your REST methods.
Also, it would be better to create a model object in REST layer for Error messages - ErrorResponse, with appropriate information:
class ErrorResponse {
int statusCode;
String errorMessage;
}
And return it's object from the exception handlers. BTW, you can also map your exception class directly to a response using #ResponseStatus annotation:
#ResponseStatus(value=401, reason="message")
class Exception_Y extends RuntimeException {
}
Then you don't have to write exception handler for that exception.
My suggestion would be wrap any unchecked exceptions with a service layer for loose coupling, and clean abstraction. Keep your controller free from conditions and let Service layer take care of this pain.
Keeping security concern in mind if you exposing it externally wrap your exception with service oriented exception it also helps to achieve generic layer specific exceptions say PersistentException, ServiceException etc. keeping good degree of decoupling in mind.
For handling exception globally you can use spring inbuild ControllerAdvice annotation with JsonExceptionModel for formatted error response.
#ControllerAdvice
public class GlobalExceptionHandler {
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(SQLException.class)
public Map<String, Object> handleSQLException(HttpServletRequest request, Exception ex) {
//json response here
}
}
public class JsonExceptionModel {
private int code;
private String type;
private String url;
private String message;
private String moreInfo;
// getters/setters here
}
I suggest you to go with Exception Resolver which is providing by spring.
Spring Framework provides HandlerExceptionResolver interface that we can implement to create global exception handler. We can also override it to create our own global handler with our application specific changes, such as logging of exception messages.
Here is the sample implementation of HandlerExceptionResolver,which will fullfill your need
public class RestResponseStatusExceptionResolver extends HandlerExceptionResolver {
#Override
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
if (ex instanceof InvalidInputException) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return handleException(ex);
} else if (ex instanceof ResourceNotFoundException) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return handleException(ex);
}
//Adding error details to modelView object
modelAndView.addObject("errors", ErrorDetails);
// Custom error message details
public class ErrorDetails {
private String code;
private List<String> data;
}
I have dao, service and action classes in my spring mvc application.
I am throwing Exception in Dao and Service classes. Now in Action, normally I have to write try catch block and in case exception occurs in dao and service, it will be thrown from there and it will go in catch block in action.
I have a error jsp which will be displayed.
Problem is I need to write same catch block in all action methods.
Is it possible to throw it again in action methods too and handle it from a single point rather than writing same code everywhere.
Please suggest.
You can also have a look at Spring Integration. It provides the use of gateways, filters and channels. Each can have a Request, Response and Error channel assigned. Or there is even a default error handler. In case all data flows through a specific channel, having a custom error handler is as simple as follows:
#MessageEndpoint
public class MyErrorHandler {
#ServiceActivator(inputChannel = "errorChannel")
public String handle(String messsage) {
// do whatever you like
}
}
The Integration framework offers lots of usefull stuff for general handling.
I think you are looking for cross-cutting exception handling and good news, you are working with Spring MVC yes you can use this feature.
All you need to do, is throw your CustomExcptions or whatever other Exceptions that are from your services to your action methods.
Let's say here is your service:
#Service
public class MyService {
public void someMethod throws RuntimeException {
...
}
}
In your controller method:
#Controller
public class MyController {
#Autowired
MyService service;
#RequestMapping("/someuri"){
try {
service.someMethod();
} catch {
throw new RuntimeException();
}
}
#ExceptionHandler(RuntimeException.class)
public ModelAndView handleException(RuntimeException ex) {
ModelAndView model = new ModelAndView("errorpage");
return model;
}
}
The handleException method annotated with ExceptionHandler is your advice method for exception handling and it will be called anytime a RuntimeException is throw inside your controller and you can keep up like this for all other exceptions.