Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot custom exception within an Rest Service

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.

like image 284
Alex Avatar asked Oct 21 '14 09:10

Alex


2 Answers

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

    };
}
like image 101
Andy Wilkinson Avatar answered Oct 19 '22 17:10

Andy Wilkinson


@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;
    }
  }
 }
like image 4
Harpreet Sandhu - TheRootCoder Avatar answered Oct 19 '22 18:10

Harpreet Sandhu - TheRootCoder