John Fadria
John Fadria

Reputation: 1945

Return Jersey exceptions in JSON

What is the best way to return Jersey exceptions in JSON format? Here my sample code.

    public static class CompoThngExceptionMapper implements ExceptionMapper<Exception> {
    @Override
    public Response toResponse(Exception exception) {
        if (exception instanceof WebApplicationException) {
            WebApplicationException e = (WebApplicationException) exception;
            Response r = e.getResponse();
            return Response.status(r.getStatus()).entity(**HERE JSON**).build();
    } else {
            return null;

        }
    }

Thanks in advance!!!

Upvotes: 7

Views: 13429

Answers (2)

Whome
Whome

Reputation: 10400

Avoiding import Jackson classes but only stick to pure JAX-RS classes I create json exception wrappers like this.

Create ExceptionInfo wrapper and subclass various exception status types.

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
@XmlRootElement
public class ExceptionInfo {
    private int status;
    private String msg, desc;
    public ExceptionInfo(int status, String msg, String desc) {
        this.status=status;
        this.msg=msg;
        this.desc=desc;
    }

    @XmlElement public int getStatus() { return status; }
    @XmlElement public String getMessage() { return msg; }
    @XmlElement public String getDescription() { return desc; }
}

- - - - 

import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.WebApplicationException;

/**
 * Create 404 NOT FOUND exception
 */
public class NotFoundException extends WebApplicationException {
    private static final long serialVersionUID = 1L;

    public NotFoundException() {
        this("Resource not found", null);
    }

    /**
     * Create a HTTP 404 (Not Found) exception.
     * @param message the String that is the entity of the 404 response.
     */
    public NotFoundException(String msg, String desc) {
        super(Response.status(Status.NOT_FOUND).entity(
                new ExceptionInfo(Status.NOT_FOUND.getStatusCode(), msg, desc)
        ).type("application/json").build());
    }

}

Then throw exceptions in a resource implementation and client receives a nice json formatted http error body.

@Path("/properties")
public class PropertyService {
    ...
    @GET @Path("/{key}")
    @Produces({"application/json;charset=UTF-8"})
    public Property getProperty(@PathParam("key") String key) {
        // 200=OK(json obj), 404=NotFound
        Property bean = DBUtil.getProperty(key);
        if (bean==null) throw new NotFoundException();
        return bean;
    }   
    ...
}

- - - - 
Content-Type: application/json
{"status":404,"message":"Resource not found","description":null}

Upvotes: 9

user1596371
user1596371

Reputation:

Depends on what you want to return, but personally I have an ErrorInfo object that looks something like this:

public class ErrorInfo {
    final transient String developerMessage;
    final transient String userMessage;

    // Getters/setters/initializer
}

which I pass around as part of my Exceptions, then I just use Jackson's ObjectMapper to create a JSON string from the ErrorInfo object in the ExceptionMapper. The nice thing with this approach is that you can extend it very easily, so adding status information, time of error, whatever, is just a case of adding another field.

Bear in mind that adding in things like the response's status is a bit of a waste, as that will be coming back in the HTTP header anyway.

Update

A complete example as follows (in this case ErrorInfo has more fields in it, but you get the general idea):

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

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

@Provider
public class UnexpectedExceptionMapper implements ExceptionMapper<Exception>
{
  private static final transient ObjectMapper MAPPER = new ObjectMapper(); 

  @Override
  public Response toResponse(final Exception exception)
  {
    ResponseBuilder builder = Response.status(Status.BAD_REQUEST)
                                      .entity(defaultJSON(exception))
                                      .type(MediaType.APPLICATION_JSON);
    return builder.build();
  }

  private String defaultJSON(final Exception exception)
  {
    ErrorInfo errorInfo = new ErrorInfo(null, exception.getMessage(), exception.getMessage(), (String)null);

    try
    {
      return MAPPER.writeValueAsString(errorInfo);
    }
    catch (JsonProcessingException e)
    {
      return "{\"message\":\"An internal error occurred\"}";
    }
  }
}

Upvotes: 10

Related Questions