Table of Contents
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 asbyte[],
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".
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.
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.
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.