Spring REST API hardening – exceptions handling

For some time now there’s a lot of discussion over the internet on the definition of a unified RESTful API standard (architecture, design, interfaces, etc.). RFC’s are being discussed by IETF committees trying to come up with a standard, work continues on re-designing the HTTP protocol itself (HTTPbis, by the IETF HTTPbis WG chaired by Mark Nottingham), but what we have thus far are only good and bad practices…

 

What i’d like to address in this post are good practices related to exceptions handling in a Java Spring MVC based RESTful API application.

 

Let’s start with this: In order to make your REST API more developer-friendly, you may want your MVC controllers to return (within the body of the response), some information that can assist the client developer while using your API.

 

Using a JSON data format, a sample controller response in situation of an exception may look like this:

{
    "code" : "SAE00202",
    "status" : "SECURITY_AUTHORITY_ERROR",
    "errors" : [ "Security Exception: Insufficient Authorization Level. Access denied" ]
}

 

Here’s an example of how you can achieve that easily using Spring MVC’s @ControllerAdvice annotation (which indicates the annotated class assists a “Controller” and serves as a specialization of @Component annotation, allowing implementation classes to be auto-detected through classpath scanning).

@ControllerAdvice
public class BusinessExceptionHandler extends DefaultExceptionHandler {

    @ExceptionHandler(IncompleteUserProfileException.class)
    @ResponseStatus(value = HttpStatus.PRECONDITION_FAILED)
    @ResponseBody DefaultErrorMessage handleIncompleteUserProfileException(IncompleteUserProfileException e) {

        if(debug) logException(e);

        String error = getResourceBundle().getMessage("exception.user.profile.incomplete", null, Locale.getDefault());

        return new DefaultErrorMessage("RS00302", "BUSINESS_ERROR", error);

    }

}

 

Aside from the fact that you may want to write custom handlers like the one above for your application-specific exceptions (Business/Security/Validation related, etc.), you may also want to “go deeper” and extend the ResponseEntityExceptionHandler class (abstract), and override the default implementation of the exceptions below; thrown usually by MVC Controllers:

Spring MVC ResponseEntityExceptionHandler Exceptions

 

In order to do that, first you have to start with a simple POJO representing your default error message:

public class DefaultErrorMessage {
    private String code;
    private String status;
    private List errors = new ArrayList<>();

    public DefaultErrorMessage(String code, String status, String error) {
        this.code = code;
        this.status = status;
        this.errors.add(error);
    }

    public DefaultErrorMessage(String code, String status, List errors) {
        this.code = code;
        this.status = status;
        this.errors = errors;
    }

    // getters and setters omitted
}

 

Having this in place, a custom implementation of BindException may look like this:

@ControllerAdvice
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

    @Override
    protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

        List<String> errors = new ArrayList<>(ex.getAllErrors().size());
        List<FieldError> fieldErrors = ex.getFieldErrors();
        StringBuilder sb;

        for (FieldError fieldError : fieldErrors) {
            sb = new StringBuilder();
            sb.append("Field: ").append(fieldError.getField()).append(", ");
            sb.append("Value: ").append(fieldError.getRejectedValue()).append(", ");
            sb.append("Message: ").append(fieldError.getDefaultMessage());
            errors.add(sb.toString());
        }

        List<ObjectError> globalErrors = ex.getGlobalErrors();

        for (ObjectError objectError : globalErrors) {
            sb = new StringBuilder();
            sb.append("Object: ").append(objectError.getObjectName()).append(", ");
            sb.append("Code: ").append(objectError.getCode()).append(", ");
            sb.append("Message: ").append(objectError.getDefaultMessage());
            errors.add(sb.toString());
        }

        DefaultErrorMessage errorMessage = new DefaultErrorMessage("RQ00051", "RQ_BODY_VALIDATION_ERROR", errors);
        return new ResponseEntity(errorMessage, headers, status);

    }

}

 

I think you see the direction it’s heading in…

 

Happy coding 🙂

Cheers!

 

 

Resources:

Advertisements

Tagged: , , , ,

One thought on “Spring REST API hardening – exceptions handling

  1. Robert June 9, 2014 at 11:27 pm Reply

    thank you, End up mapping most of the exceptions there…cheers

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: