Spring boot + REST (Error Handling)


One of the most important points while we are developing a REST api is the error handling, in this post we will explain how to implement it by using Spring boot + Jersey. Te example that we are going to use as base is Spring Boot + REST Jersey Part 1.

Step 1:  Creating classes to represent the errors

The first step will be to create the necessary classes to represent the errors, in this case we will create two classes ErrorMessage and ServiceException.

ErrorMessage.java

package com.raidentrance.model;

import java.io.Serializable;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;

/**
 * @author raidentrance
 *
 */
@JsonInclude(Include.NON_NULL)
public class ErrorMessage implements Serializable {
    private Integer httpStatus;
    private String message;
    private Integer code;
    private String developerMessage;

    private static final long serialVersionUID = 5318063708359922770L;

    public ErrorMessage() {
    }

    public ErrorMessage(ServiceException ex) {
        this.httpStatus = ex.getHttpStatus();
        this.message = ex.getMessage();
        this.code = ex.getCode();
    }

    public ErrorMessage(Integer httpStatus, String message, Integer code) {
        super();
        this.httpStatus = httpStatus;
        this.message = message;
        this.code = code;
    }

    public Integer getHttpStatus() {
        return httpStatus;
    }

    public void setHttpStatus(Integer httpStatus) {
        this.httpStatus = httpStatus;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getDeveloperMessage() {
        return developerMessage;
    }

    public void setDeveloperMessage(String developerMessage) {
        this.developerMessage = developerMessage;
    }

}

The error class will be used to represent the errors in JSON format and it contains the following attributes:

  • httpStatus: Will contain the http status that will be returned
  • message: It is used to show a small error message that describes the problem
  • code: It is used to show a small error code that can be representative for an application
  • developerMessage: You can specify the generated exception in order to make easier than the developer find the problem

The annotation @JsonInclude(Include.NON_NULL) specify that if an attribute is null it won’t be in the response.

ServiceException.java

/**
 *
 */
package com.raidentrance.model;

/**
 * @author raidentrance
 *
 */

public class ServiceException extends Exception{

    private Integer httpStatus;
    private String message;
    private Integer code;
    private String developerMessage;

    private static final long serialVersionUID = -528134378438377740L;

    public ServiceException(Integer httpStatus, String message, Integer code,String developerMessage) {
        this.httpStatus = httpStatus;
        this.message = message;
        this.code = code;
        this.developerMessage=developerMessage;
    }

    public ServiceException(Integer httpStatus, String message, Integer code) {
        this.httpStatus = httpStatus;
        this.message = message;
        this.code = code;
    }

    public ServiceException(ErrorMessage errorMessage){
        this.httpStatus = errorMessage.getHttpStatus();
        this.message = errorMessage.getMessage();
        this.code = errorMessage.getCode();
        this.developerMessage=errorMessage.getDeveloperMessage();
    }

    public String getDeveloperMessage() {
        return developerMessage;
    }

    public void setDeveloperMessage(String developerMessage) {
        this.developerMessage = developerMessage;
    }

    public Integer getHttpStatus() {
        return httpStatus;
    }

    public void setHttpStatus(Integer httpStatus) {
        this.httpStatus = httpStatus;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

}

The ServiceException class is an exception and will be used to throw the errors in the application, as you can see it receives the necessary parameters to populate the ErrorMessage class.

Step 2: Creating exception mappers

The next step is to create the exception mappers, it will define what to do if an exception happens. In this example we will generate 2 exception mappers one in case that a ServiceException happens and the other in case that a Throwable happens to handle any unconsidered error.

ServiceExceptionMapper.java

package com.raidentrance.error;

import java.io.PrintWriter;
import java.io.StringWriter;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import com.raidentrance.model.ErrorMessage;
import com.raidentrance.model.ServiceException;

/**
 * @author raidentrance
 *
 */

@Provider
public class ServiceExceptionMapper implements ExceptionMapper<ServiceException> {

    @Override
    public Response toResponse(ServiceException ex) {
        ErrorMessage errorMessage = new ErrorMessage();
        errorMessage.setCode(ex.getCode());
        errorMessage.setMessage(ex.getMessage());
        StringWriter errorStackTrace = new StringWriter();
        ex.printStackTrace(new PrintWriter(errorStackTrace));
        errorMessage.setDeveloperMessage(ex.toString());
        return Response.status(ex.getHttpStatus()).entity(errorMessage).type(MediaType.APPLICATION_JSON).build();
    }

}

To create an exception mapper we just need to implement the ExceptionMapper interface and specify in the generic part the type of the exception that we are going to handle, in this case we are specifying ServiceException.

GenericExceptionMapper.java

package com.raidentrance.error;

import java.io.PrintWriter;
import java.io.StringWriter;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

import org.springframework.http.HttpStatus;

import com.raidentrance.model.ErrorMessage;

/**
 * @author raidentrance
 *
 */
@Provider
public class GenericExceptionMapper implements ExceptionMapper<Throwable> {

    public Response toResponse(Throwable ex) {
        ErrorMessage errorMessage = new ErrorMessage();
        errorMessage.setMessage(ex.getMessage());
        StringWriter errorStackTrace = new StringWriter();
        ex.printStackTrace(new PrintWriter(errorStackTrace));
        errorMessage.setDeveloperMessage(ex.toString());
        return Response.status(HttpStatus.INTERNAL_SERVER_ERROR.value()).entity(errorMessage)
                .type(MediaType.APPLICATION_JSON).build();
    }

}

In the same way we are creating another exception mapper but now with the class Throwable, it means that if any other kind of error happen it will be cached by this exception mapper, it will be used for any exception that is not handled by the application, for this reason we will return an http status 500 that means internal server error.

Step 3: Registering the Exception Mappers

Once we created the exception mappers we need to register them in the application. We are going to do it in the same way that we define another endpoint in the application in the class JerseyConfig.

/**
 *
 */
package com.raidentrance.config;

import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.stereotype.Component;

import com.raidentrance.error.GenericExceptionMapper;
import com.raidentrance.error.ServiceExceptionMapper;
import com.raidentrance.resource.UserResource;

/**
 * @author raidentrance
 *
 */
@Component
public class JerseyConfig extends ResourceConfig {
    public JerseyConfig() {
        register(UserResource.class);
        register(ServiceExceptionMapper.class);
        register(GenericExceptionMapper.class);
    }
}

Step 4: Throwing test exceptions

To test that the exception mappers are working correct we are going to create 2 endpoints one will throe a ServiceException and the other will throw a NullPointerException.

  • Endpoint that returns a ServiceException:
@GET
@Path("/error")
public Response sampleError() throws ServiceException {
    throw new ServiceException(HttpStatus.NOT_FOUND.value(), "Sample Error Message", 1);
}

If we execute the previous code, we will receive the following response with http status 404:

Captura de pantalla 2017-09-11 a las 9.26.56 a.m.

  • Endpoint that returns a NullPointerException:
@GET
@Path("/error/generic")
public Response sampleGenericError() {
    throw new NullPointerException();
}

If we execute the previous code, we will receive the following response with http status 500:

Captura de pantalla 2017-09-11 a las 9.29.47 a.m.

If you want to learn more about web services REST and Spring boot we recommend the following books:

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s