1

I have an API built with Spring Boot. By default the default JSON structure when an error is thrown by Spring is;

{
  "timestamp": 1477425179601,
  "status": 404,
  "error": "Not Found",
  "message": "No message available",
  "path": "/categoriess"
}

This structure is different to error responses returning myself in the API, so I'd like to change Spring to use the same structure as my own for consistency.

My error response are structured like this;

{
    "errors": [
        {
            "code": 999404,
            "message": "The resource you were looking for could not be found"
        }
    ]
}

How would I go about doing this? I've tried using an Exception Handler, but I can't figure out the correct exception to set it up for. I'd like to also make sure that the Http status is still correctly returned as 404, or whatever the error is (500 etc).

SheppardDigital
  • 3,165
  • 8
  • 44
  • 74

2 Answers2

1

I had another look at this and did manage to put something together that works for me.

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

                Map<String, Object> error = new HashMap<>();
                error.put("code", errorAttributes.get("status"));
                error.put("message", errorAttributes.get("error"));

                Map<String, Object> errorResponse = new HashMap<>();
                errorResponse.put("errors", error);

                return errorResponse;
            }
        };
    }

This returns the following JSON response along with whatever header/http status code spring was going to return.

{
  "errors": {
    "code": 404,
    "message": "Not Found"
  }
}

This seems to work great for errors generated by spring, while my own Exceptions I'm handling in Controllers or in a specific ControllerAdmin class with ExceptionHandlers.

SheppardDigital
  • 3,165
  • 8
  • 44
  • 74
0

A possible way to do something like this is to use the @ExceptionHandler annotation to create a handler method inside your controller.

@RestController
@RequestMapping(produces = APPLICATION_JSON_VALUE)
public class MyController {

    @RequestMapping(value = "/find", method = GET)
    public Object find() {
        throw new UnsupportedOperationException("Not implemented yet!");
    }

    @ExceptionHandler
    public ErrorListModel handleException(Exception exception) {
        ExceptionModel exceptionModel = new ExceptionModel(1337, exception.getMessage());

        ErrorListModel list = new ErrorListModel();
        list.add(exceptionModel);

        return list;
    }

    private class ErrorListModel {
        private List<ExceptionModel> errors = new ArrayList<>();

        public void add(ExceptionModel exception) {
            errors.add(exception);
        }

        public List<ExceptionModel> getErrors() {
            return errors;
        }
    }

    private class ExceptionModel {
        private int code;
        private String message;

        public ExceptionModel(int code, String message) {
            this.code = code;
            this.message = message;
        }

        public int getCode() {
            return code;
        }

        public String getMessage() {
            return message;
        }
    }
}

The private classes ErrorListModel and ExceptionModel just help defining how the resulting JSON body should look, and I assume you already have your own, similar classes.

The find method just throws an exception for us to handle, which gets intercepted by the handleException method because it's annotated with @ExceptionHandler. In here, we create an ExceptionModel, populate it with information from the original exception, and add it to an ErrorListModel, which we then return.

This blog post from 2013 explains the features better than I ever could, and it also mentions an additional option, @ControllerAdvice. It basically allows you to re-use the exception handling in other controllers as well.

Thomas Kåsene
  • 5,301
  • 3
  • 18
  • 30
  • Thanks. In my project I have an ExceptionConntroller that's annotated with ControllerAdvice, in there I put ExceptionHandlers. I had already tried to create an Exception handler, but sadly I don't seem to be able to catch Spring standard 404 Not Found response. I tried your example above, and it didn't catch it either. – SheppardDigital Oct 26 '16 at 07:15
  • Then this post might hold some answers for you: http://stackoverflow.com/questions/28902374 – Thomas Kåsene Oct 26 '16 at 09:54