could you guys share some advices on how to design a good implementation for handling exception in Spring MVC? Here are some of my thoughts after spending some time online trying to figure out the suitable or better implementation in handling exception.
Here is some background on the project I m working on:
- The MVC framework used is Spring Boot Web. Similar to other projects using Spring MVC, I have separated my codes into few layers: controller, services, and model.
- In the controller, it is mainly to validate the form inputs from the view(front-end) layer and perform some business logic. For form validation, I m using using the Spring validation annotation(JSR 303) and it integrates perfectly fine with the controller.
- But, when it comes to the business logic implementation which are distributed across the controller and services layer, I would want to handle some checking which might terminate the execution of the controller/service method block if it fails the check.
- So, i think the best would be to use exception for handling this.
- There are a few implementation that I found online that share on how to implement a better exception handling framework using Spring existing exception handling mechanism. E.g. controller advice and controller based exception handler, etc.
So, here is my confusion. There are two types of exceptions handling mechanism that I have thought of. Both were implemented using @ControllerAdvice, which separates the exception handling in a separate Java class.
Approach A
A same exception is reused for a particular method in a mvc controller. For example,
public String methodA() throws AException{ // bla bla bla }
Then there will be unique AExceptionHandler handling this exception in a separate java file. There could be other method call in methodA() that throws different Exception. But, a try catch block would be implemented to wrap the method throwing other type of Exception and re-throw with AException.
- So, it is sort of reusing AException for that methodA. Of cause, there will be some addition attributes for the BaseException class to store the details on return messages(to be displayed on view) and return viewname.
- But, this will cause the controller or service layer to be cluttered with return message or return viewname.
Approach B
- The other uses unique exceptions in controller or service layer. But, a designated exception handler will be implemented for each exception.
- Since the exception is unique, the details(return message and viewname) can be placed at the exception handler, without polluting the controller layer. They will cleaner in this way. But, there will be alot more boilerplate code just for exception handler and many exceptions for different unique cases that I wish to terminate the code execution for a particular method block.
So, these are issues I m facing now. I hope that I'm moving in the correct direction in terms of handling exception for Spring web. Please let me know you thoughts on this. Thanks.
Example:
Lets use the account signup method as example:
//In the controller class
public String accountSignUp(@Valid AccountSignUpForm form){
if(bindingResult.hasErrors()){
return "signupview";
}
accountSignUpSvc.validateEmail(form.getEmail);
accountSignUpSvc.createAccount(form);
}
// In the accountSignUpSvc class;
public interface AccountSignUpSvc {
// Check if email has been used for sign up.
void validateEmail(String email) throws DuplicateAccountException;
// Create the account based on the form.
void createAccount(AccountSignUpForm form) throws AccountCreationException;
}
So using approach A:
//In the controller class
public String accountSignUp(@Valid AccountSignUpForm form){
if(bindingResult.hasErrors()){
return "signupview";
}
try{
accountSignUpSvc.validateEmail(form.getEmail);
} catch ( DuplicateAccountException e){
// Rethrow with a controller method specific exception
throw new AccountSignUpException("Returned error message"," Internal error message.",e);
}
// Then implement the similar try catch block for the accountSignUpSvc.createAccount method
}
@ControllerAdvice
public class AccountSignUpExceptionHandler{
@ExceptionHandler(AccountSignUpException.class)
public ModelAndView handleAccountSignUpException(HttpServletRequest request, AccountSignUpException ex){
log.error(ex.getInternalError);
ModelAndView mav = new ModelAndView("signup");
mav.addObject("returnError", ex.getReturnError());
return mav;
}
Then using approach B:
//In the controller class
public String accountSignUp(@Valid AccountSignUpForm form){
if(bindingResult.hasErrors()){
return "signupview";
}
accountSignUpSvc.validateEmail(form.getEmail);
// Then implement the similar try catch block for the accountSignUpSvc.createAccount method
}
@ControllerAdvice
public class AccountSignUpExceptionHandler{
@ExceptionHandler(DuplicateAccountException.class)
public ModelAndView handleDuplicateAccountException(HttpServletRequest request, DuplicateAccountException ex){
log.error("Error due to error");
ModelAndView mav = new ModelAndView("signup");
mav.addObject("returnError", "Return error message: a long error message.");
return mav;
}
The benefit I mentioned is that the exception handler can be specific as only a certain condition would trigger it. So, any long return message that is supposed to be displayed at the html page can be coded in this specific exception handler.