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:
- 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:
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