Monday, February 15, 2010

JAX-RS and HTTP Responses

HTTP Status Codes are a big part of the HTTP protocol that most of us have seen countless times in our web browsing and development. We are used to seeing 404 (Not Found), 200 (OK), and so forth. Because HTTP is often closely tied with REST style applications, it is not surprising that JAX-RS provides tremendous support for returning appropriate HTTP responses from REST-based web services. In this blog post, I look at the tip of the iceberg in terms of JAX-RS support for HTTP-based service responses.

The code listing that follows would not generally be a useful piece of functionality, but it is used here to demonstrate how easy it is with JAX-RS to generate HTTP responses with specific HTTP status codes.


@Path("/responses")
public class ResponseMaker
{
/**
* Generates an HTTP response based on the String provided as part of this
* resource's URI.
*
* @param responseString Portion of this resource's URI from which the
* particular response will be generated.
* @return Response based on provided URI portion.
*/
@GET
@Path("/{responseString}")
@Consumes("text/plain")
@Produces("text/html")
public Response getResponse(
@PathParam("responseString") final String responseString)
{
final String desiredResponse = responseString != null
? responseString.trim().toUpperCase()
: Status.BAD_REQUEST.name();
Status status = null;
try
{
status = Status.valueOf(desiredResponse);
}
catch (IllegalArgumentException illegalArgEx)
{
status = Status.BAD_REQUEST;
}
return Response.status(status).type(MediaType.TEXT_HTML_TYPE).build();
}
}


The above code uses JAX-RS annotations to indicate which HTTP method corresponds to the Java method in the code listing (@GET indicates HTTP GET method). This simple method accepts a String and, if the String matches one of the Response.Status enum value's string representations, that Status is set. Otherwise, a default of BAD_REQUEST is selected. The Response is built with that status and with a MediaType of TEXT_HTML_TYPE.

I use RESTClient as an easy client tool to demonstrate this simple JAX-RS web service in action. The next two screen snapshots show what RESTClient sees (and what any other client would see) in terms of responses to certain provided URIs (shown at top of GUI).






JAX-RS also makes it easy to turn encountered exceptions into HTTP response codes indicating the error. The next piece of code demonstrates this.


/**
* Return an exception-based HTTP response based on the provided number
* indicating a particular exception to be used. The HTTP method PUT would
* normally likely not be the method used for this type of operation, but
* it makes it easy to differentiate from the other method in this class
* already tied to @GET and also accepting a single String.
*
* There are four cases in which a particular exception is used to build the
* response and that exception and a particular Response.Status are provided,
* telling the JAX-RS provider which HTTP status to tie to that particular
* thrown exception. In the default/general case when one of the first four
* are not used, no specific Response.Status is used, so the general 500
* Internal Server Error will be returned to the client along with the'
* exception's stack trace as the body.
*
* @param exceptionNumberType A number used to determine which type of
* exception is used for the basis of the response.
* @return Response based on the described exception type.
*/
@PUT
@Path("/{exceptionNumberType}")
@Consumes("text/plain")
public Response causeException(
@PathParam("exceptionNumberType") final int exceptionNumberType)
{
Exception exception;
Status status = null;
switch (exceptionNumberType)
{
case 1 : exception = new NullPointerException("1. Null Encountered.");
status = Status.NOT_FOUND;
break;
case 2 : exception = new IllegalArgumentException("2. Bad argument");
status = Status.PRECONDITION_FAILED;
break;
case 3 : exception = new RuntimeException("3. Runtime Exception");
status = Status.BAD_REQUEST;
break;
case 4 : exception = new NumberFormatException("4. Bad Numeric Format");
status = Status.NOT_ACCEPTABLE;
break;
default : exception = new Exception("General Exception");
}
throw status != null
? new WebApplicationException(exception, status)
: new WebApplicationException(exception);
}


I included some verbose comments on this method to describe how it behaves in greater detail. This method primarily demonstrates how the WebApplicationException is useful in turning Java exceptions into HTTP responses. I only use two of this exception's eight constructors in this example, but they demonstrate the difference between providing a particular HTTP Response.Status with the exception (and counting on the JAX-RS provider to place the results of Response.Status.getReasonPhrase() in the response body) or allowing the JAX-RS provider to associated a Response.Status with the exception (500) and showing the entire stack trace of the exception.

This difference is demonstrated in the next three screen snapshots. Two are of exceptions for which a particular (largely nonsensical here and only meant to illustrate how to do this) HTTP status code is associated with the exception and one is for the case where the JAX-RS provider assigns 500 implicitly and puts the exception's stack trace in the response body rather than the Response.Status.getReasonPhrase().







In this post, I have attempted to demonstrate how easy JAX-RS makes it to specify HTTP responses based on regular and exceptional conditions. I have only touched on a small part of the extensive JAX-RS support for building appropriate responses.

No comments: