14

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.

2 Answers 2

22

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; } }; } 
Sign up to request clarification or add additional context in comments.

2 Comments

Hi Andy, here If custom exception class extends with Exception or Throwable then error instance would be UnhandledThrowException why it is happening.
@KSK It's not clear to me how that's related to this question an answer. I think you'd be better asking a new question with some more details
4

@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; } } } 

4 Comments

I think I like this approach better than redefining ErrorAttributes globally. Although I think it should ideally still utilize the global ErrorAttributes to obtain the default attributes, and then put extra values in.
@M. Prokhorov agreed, that will make it more powerful approach
@HarpreetSandhu-TheCoder when I try to access this from a java program, it returns the error code, but not the error message. ie, it returns the error code as 404, but the message is not found anywhere in the response object.
@kingkari what are you trying to access using java program? can you please elaborate your question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.