Generic Rest Service Jersey Error Handling
This tutorial explains the best practices how to handle jersey error handling. Basically we create a generic exception handler that will handle all exceptions thrown by any methods inside a rest service.
Dependencies
Add the dependencies to your project.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.memorynotfound.ws.rs.jersey</groupId>
<artifactId>error-handling</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>JERSEY - ${project.artifactId}</name>
<url>https://memorynotfound.com</url>
<packaging>war</packaging>
<properties>
<jersey.version>2.18</jersey.version>
</properties>
<dependencies>
<!-- jersey implementation library -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey.version}</version>
</dependency>
<!-- servlet provided by tomcat -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
Jersey Rest Service
For testing purposes we created a rest service that will trigger an RuntimeException
.
package com.memorynotfound.rs;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/monitor")
@Produces(MediaType.APPLICATION_JSON)
public class MonitorRestService {
@GET
@Path("/check")
public String checkMonitor() {
throw new RuntimeException("Monitor not available");
}
}
Custom Error Output
This object represents the error output that’ll be send to the client.
package com.memorynotfound.rs.exception;
import com.fasterxml.jackson.annotation.JsonRootName;
@JsonRootName(value = "error")
public class Error {
private int statusCode;
private String statusDescription;
private String errorMessage;
public Error(int statusCode, String statusDescription, String errorMessage) {
this.statusCode = statusCode;
this.statusDescription = statusDescription;
this.errorMessage = errorMessage;
}
public int getStatusCode() {
return statusCode;
}
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
public String getStatusDescription() {
return statusDescription;
}
public void setStatusDescription(String statusDescription) {
this.statusDescription = statusDescription;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}
Generic Jersey Error Handling
We create a GenericExceptionMapper
that implements the ExceptionMapper
interface. This implies us to override the toResponse()
method. In this method we can create a response object that’ll be triggered when an exception occurs.
package com.memorynotfound.rs.exception.mapper;
import com.memorynotfound.rs.exception.Error;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
@Provider
public class GenericExceptionMapper implements ExceptionMapper {
@Override
public Response toResponse(Throwable ex) {
Response.StatusType type = getStatusType(ex);
Error error = new Error(
type.getStatusCode(),
type.getReasonPhrase(),
ex.getLocalizedMessage());
return Response.status(error.getStatusCode())
.entity(error)
.type(MediaType.APPLICATION_JSON)
.build();
}
private Response.StatusType getStatusType(Throwable ex) {
if (ex instanceof WebApplicationException) {
return((WebApplicationException)ex).getResponse().getStatusInfo();
} else {
return Response.Status.INTERNAL_SERVER_ERROR;
}
}
}
Servlet Descriptor
- Register the
ServletContainer
in yourweb.xml
. - Add a init-param for which packages jersey must scan to look for rest services to manage.
- Create a servlet-mapping on which the
ServletContainer
must listen.
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">
<servlet>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.memorynotfound.rs</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
</web-app>
Demo
URL: http://localhost:8080/jersey-error-handling/api/monitor/check
URL: http://localhost:8080/jersey-error-handling/api/monitor/not-available
Hi ,
Your tutorial is crisp and clear. However I have one question. I want to have a response something like below json structure
ProblemDetails {
status:405
code:1
message:”user not found”
}
I tried to achieve this by embedding ProblemDetails object in Error class, but it didn’t worked for me.
Any help will be appreciated in this regard, thanks a lot.
In order to achieve wrapping, you need to configure the correct feature in the
objectMapper
.Here is an example how to customize the objectmapper.