There are several reasons why we should manage exceptions such as:
- Customize an error page
- Business logic
- Reduce logic
The common way to manage exceptions in Spring is using an HandlerExceptionResolver class, in this example I will show you how to manage an generic exception in our code.
package com.jos.dem.jmailer.controller
import org.springframework.web.servlet.HandlerExceptionResolver
import org.springframework.http.HttpStatus
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import org.springframework.stereotype.Component
import org.springframework.http.ResponseEntity
import org.springframework.web.servlet.ModelAndView
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
@Component
class HandlerException implements HandlerExceptionResolver {
Log log = LogFactory.getLog(this.class)
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex){
log.info ex.message
def data = [:]
data.message = ex.message
ModelAndView modelAndView = new ModelAndView("error")
modelAndView.addObject("data", data)
modelAndView
}
}
Here we have an HandlerException class that implements HandlerExceptionResolver, and a resolveException method who receives exception, render to the error page and send an data as a Map with some information we want user sees.
Our error page looks like this:
File: $PROJECT_HOME/src/main/resources/templates/error.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout">
<head>
<title>Jmailer Error page</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<div th:if="${data}">
<p th:text="'Error: ' + ${data.message}" />
</div>
</body>
</html>
To get this work, you only need to add Thymeleaf dependency to your build.gradle.
compile 'org.springframework.boot:spring-boot-starter-thymeleaf'
Another common way to handle exceptions is to use @ExceptionHandler
annotation in a method in a controller, I normally get this option when I’m builing an API.
package com.jos.dem.jmailer.controller
import static org.springframework.web.bind.annotation.RequestMethod.POST
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.http.ResponseEntity
import org.springframework.http.HttpStatus
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import com.jos.dem.jmailer.service.EmailerService
import com.jos.dem.jmailer.command.MessageCommand
import com.jos.dem.jmailer.exception.BusinessException
@RestController
class EmailerController {
@Autowired
EmailerService emailerService
@Value('${email.redirect}')
String redirectUrl
Logger logger = LoggerFactory.getLogger(this.class)
@RequestMapping(method = POST, value = "/message", consumes="application/json")
ResponseEntity<String> message(@RequestBody MessageCommand command) {
logger.info "Sending contact email: ${command.email}"
emailerService.sendEmail(command)
new ResponseEntity<String>("OK", HttpStatus.OK)
}
@ResponseStatus(value=HttpStatus.UNAUTHORIZED, reason="Unauthorized")
@ExceptionHandler(BusinessException.class)
ResponseEntity<String> handleException(BusinessException be){
return new ResponseEntity<String>("Unauthorized", HttpStatus.UNAUTHORIZED)
}
}
That’s it, when a BusinessException occurs the method handleException will respond with a 401 UNAUTHORIZED
to the client. In this way we avoid to use a try
catch
estructure to handle exceptions.
To download the project:
git clone https://github.com/josdem/jmailer-spring-boot.git
git fetch
git checkout feature/handler-exception