Links: Table of Contents | Single HTML

Chapter 4. Representations and Responses

4.1. Representations and Java Types

Previous sections on @Produces and @Consumes referred to MIME media types of representations and showed resource methods that consume and produce the Java type String for a number of different media types. However, String is just one of many Java types that are required to be supported by JAX-RS implementations.

Java types such as byte[], java.io.InputStream, java.io.Reader and java.io.File are supported. In addition JAXB beans are supported. Such beans are JAXBElement or classes annotated with @XmlRootElement or @XmlType. The samples jaxb and json-from-jaxb show the use of JAXB beans.

Unlike method parameters that are associated with the extraction of request parameters, the method parameter associated with the representation being consumed does not require annotating. A maximum of one such unannotated method parameter may exist since there may only be a maximum of one such representation sent in a request.

The representation being produced corresponds to what is returned by the resource method. For example JAX-RS makes it simple to produce images that are instance of File as follows:

Example 4.1. Using File with a specific MIME type to produce a response

@GET
@Path("/images/{image}")
@Produces("image/*")
public Response getImage(@PathParam("image") String image) {
    File f = new File(image);

    if (!f.exists()) {
        throw new WebApplicationException(404);
    }

    String mt = new MimetypesFileTypeMap().getContentType(f);
    return Response.ok(f, mt).build();
}


A File type can also be used when consuming, a temporary file will be created where the request entity is stored.

The Content-Type (if not set, see next section) can be automatically set from the MIME media types declared by @Produces if the most acceptable media type is not a wild card (one that contains a *, for example "application/" or "/*"). Given the following method, the most acceptable MIME type is used when multiple output MIME types allowed:

@GET
@Produces({"application/xml", "application/json"})
public String doGetAsXmlOrJson() {
    ...
}

If "application/xml" is the most acceptable then the Content-Type of the response will be set to "application/xml".

4.2. Building Responses

Sometimes it is necessary to return additional information in response to a HTTP request. Such information may be built and returned using Response and Response.ResponseBuilder. For example, a common RESTful pattern for the creation of a new resource is to support a POST request that returns a 201 (Created) status code and a Location header whose value is the URI to the newly created resource. This may be achieved as follows:

Example 4.2. Returning 201 status code and adding Location header in response to POST request

@POST
@Consumes("application/xml")
public Response post(String content) {
    URI createdUri = ...
    create(content);
    return Response.created(createdUri).build();
}


In the above no representation produced is returned, this can be achieved by building an entity as part of the response as follows:

Example 4.3. Adding an entity body to a custom response

@POST
@Consumes("application/xml")
public Response post(String content) {
    URI createdUri = ...
    String createdContent = create(content);
    return Response.created(createdUri).entity(createdContent).build();
}


Response building provides other functionality such as setting the entity tag and last modified date of the representation.

4.3. WebApplicationException and Mapping Exceptions to Responses

Previous sections have shown how to return HTTP responses and it is possible to return HTTP errors using the same mechanism. However, sometimes when programming in Java it is more natural to use exceptions for HTTP errors.

The following example shows the throwing of a NotFoundException from the bookmark sample:

Example 4.4. Throwing Jersey specific exceptions to control response

@Path("items/{itemid}/")
public Item getItem(@PathParam("itemid") String itemid) {
    Item i = getItems().get(itemid);
    if (i == null)
        throw new NotFoundException("Item, " + itemid + ", is not found");

    return i;
}


This exception is a Jersey specific exception that extends WebApplicationException and builds a HTTP response with the 404 status code and an optional message as the body of the response:

Example 4.5. Jersey specific exception implementation

public class NotFoundException extends WebApplicationException {

    /**
     * Create a HTTP 404 (Not Found) exception.
     */
    public NotFoundException() {
        super(Responses.notFound().build());
    }

    /**
     * Create a HTTP 404 (Not Found) exception.
     * @param message the String that is the entity of the 404 response.
     */
    public NotFoundException(String message) {
        super(Response.status(Responses.NOT_FOUND).
                entity(message).type("text/plain").build());
    }

}


In other cases it may not be appropriate to throw instances of WebApplicationException, or classes that extend WebApplicationException, and instead it may be preferable to map an existing exception to a response. For such cases it is possible to use the ExceptionMapper<E extends Throwable> interface. For example, the following maps the EntityNotFoundException to a HTTP 404 (Not Found) response:

Example 4.6. Mapping generic exceptions to responses

@Provider
public class EntityNotFoundMapper implements
        ExceptionMapper<javax.persistence.EntityNotFoundException> {
    public Response toResponse(javax.persistence.EntityNotFoundException ex) {
        return Response.status(404).
            entity(ex.getMessage()).
            type("text/plain").
            build();
    }
}


The above class is annotated with @Provider, this declares that the class is of interest to the JAX-RS runtime. Such a class may be added to the set of classes of the Application instance that is configured. When an application throws an EntityNotFoundException the toResponse method of the EntityNotFoundMapper instance will be invoked.

4.4. Conditional GETs and Returning 304 (Not Modified) Responses

Conditional GETs are a great way to reduce bandwidth, and potentially server-side performance, depending on how the information used to determine conditions is calculated. A well-designed web site may return 304 (Not Modified) responses for the many of the static images it serves.

JAX-RS provides support for conditional GETs using the contextual interface Request.

The following example shows conditional GET support from the sparklines sample:

Example 4.7. Conditional GET support

public SparklinesResource(
        @QueryParam("d") IntegerList data,
        @DefaultValue("0,100") @QueryParam("limits") Interval limits,
        @Context Request request,
        @Context UriInfo ui) {
    if (data == null)
        throw new WebApplicationException(400);

    this.data = data;

    this.limits = limits;

    if (!limits.contains(data))
        throw new WebApplicationException(400);

    this.tag = computeEntityTag(ui.getRequestUri());
    if (request.getMethod().equals("GET")) {
        Response.ResponseBuilder rb = request.evaluatePreconditions(tag);
        if (rb != null)
            throw new WebApplicationException(rb.build());
    }
}


The constructor of the SparklinesResouce root resource class computes an entity tag from the request URI and then calls the request.evaluatePreconditions with that entity tag. If a client request contains an If-None-Match header with a value that contains the same entity tag that was calculated then the evaluatePreconditions returns a pre-filled out response, with the 304 status code and entity tag set, that may be built and returned. Otherwise, evaluatePreconditions returns null and the normal response can be returned.

Notice that in this example the constructor of a resource class can be used perform actions that may otherwise have to be duplicated to invoked for each resource method.