I have defined a global exception handling in my Spring Boot based Rest service:
@ControllerAdvice
public class GlobalExceptionController {
private final Logger LOG = LoggerFactory.getLogger(getClass());
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR, reason = "Internal application error")
@ExceptionHandler({ServiceException.class})
@ResponseBody
public ServiceException serviceError(ServiceException e) {
LOG.error("{}: {}", e.getErrorCode(), e.getMessage());
return e;
}
}
and a custom ServiceException:
public class ServiceException extends RuntimeException {
private static final long serialVersionUID = -6502596312985405760L;
private String errorCode;
public ServiceException(String message, String errorCode, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
}
// other constructors, getter and setters omitted
}
so far so good, when an exception is fired the controller works as it should and respond with:
{
"timestamp": 1413883870237,
"status": 500,
"error": "Internal Server Error",
"exception": "org.example.ServiceException",
"message": "somthing goes wrong",
"path": "/index"
}
but the field errorCode isn't shown in the JSON response.
So how can I define a custom exception response in my application.
Spring Boot uses an implementation of ErrorAttributes
to populate the Map
that's rendered as JSON. By default, an instance of DefaultErrorAttributes
is used. To include your custom errorCode
you'll need to provide a custom ErrorAttributes
implementation that knows about ServiceException
and its error code. This custom implementation should be an a @Bean
in your configuration.
One approach would be to sub-class DefaultErrorAttributes
:
@Bean
public ErrorAttributes errorAttributes() {
return new DefaultErrorAttributes() {
@Override
public Map<String, Object> getErrorAttributes(
RequestAttributes requestAttributes,
boolean includeStackTrace) {
Map<String, Object> errorAttributes = super.getErrorAttributes(requestAttributes, includeStackTrace);
Throwable error = getError(requestAttributes);
if (error instanceof ServiceException) {
errorAttributes.put("errorCode", ((ServiceException)error).getErrorCode());
}
return errorAttributes;
}
};
}
@Alex You can use annotation @ExceptionHandler(YourExceptionClass.class) to handle the specific exception in specific RestController. I think it's a better way to handle complicated scenarios in business applications. Moreover i will suggest you to use custom exception translator to deal with different type of exceptions. You can consider spring oauth2 exception translator as reference code for exception translator.
Note: Following code is only to understand concept of this solution. It's not production ready code. Feel free to discuss more about it.
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
/**
* @author Harpreet
* @since 16-Aug-17.
*/
@RestController
public class RestTestController {
@RequestMapping(value = "name", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ResponseObject name(@RequestParam(value="name") String value){
//your custom logic
if (value == null || value.length() < 2) {
//throwing exception to invoke customExceptionHandler
throw new NullPointerException("value of request_param:name is invalid");
}
ResponseObject responseObject = new ResponseObject();
responseObject.setMessage(value)
.setErrorCode(1);
return responseObject;
}
// to handle null pointer exception
@ExceptionHandler(NullPointerException.class)
public ResponseObject customExceptionHandler
(NullPointerException e) {
ResponseObject responseObject = new ResponseObject();
responseObject.setMessage(e.getMessage())
.setErrorCode(-1);
return responseObject;
}
// response data transfer class
static class ResponseObject{
String message;
Integer errorCode;
public String getMessage() {
return message;
}
public ResponseObject setMessage(String message) {
this.message = message;
return this;
}
public Integer getErrorCode() {
return errorCode;
}
public ResponseObject setErrorCode(Integer errorCode) {
this.errorCode = errorCode;
return this;
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With