RESTful Java with JAX-RS 2.0 (2013)
Part II. JAX-RS Workbook
Chapter 23. Examples for Chapter 9
In Chapter 9, you learned that clients can use HTTP Content Negotiation to request different data formats from the same URL using the Accept
header. You also learned that JAX-RS takes the Accept
header into account when deciding how to dispatch an HTTP request to a Java method. In this chapter, you’ll see two different examples that show how JAX-RS and HTTP conneg can work together.
Example ex09_1: Conneg with JAX-RS
This example is a slight modification from ex06_1 and shows two different concepts. First, the same JAX-RS resource method can process two different media types. Chapter 9 gives the example of a method that returns a JAXB annotated class instance that can be returned as either JSON or XML. We’ve implemented this in ex09_1 by slightly changing the CustomerResource.getCustomer()
method:
src/main/java/com/restfully/shop/services/CustomerResource.java
@Path("/customers")
public
class
CustomerResource
{
...
@GET
@Path("{id}")
@Produces({"application/xml",
"application/json"})
public
Customer
getCustomer(@PathParam("id")
int
id)
{
...
}
The JAXB provider that comes with RESTEasy can convert JAXB objects to JSON or XML. In this example, we have added the media type application/json
to getCustomer()
’s @Produces
annotation. The JAX-RS runtime will process the Accept
header and pick the appropriate media type of the response for getCustomer()
. If the Accept
header is application/xml
, XML will be produced. If the Accept
header is JSON, the Customer
object will be outputted as JSON.
The second concept being highlighted here is that you can use the @Produces
annotation to dispatch to different Java methods. To illustrate this, we’ve added the getCustomerString()
method, which processes the same URL as getCustomer()
but for a different media type:
@GET
@Path("{id}")
@Produces("text/plain")
public
Customer
getCustomerString(@PathParam("id")
int
id)
{
return
getCustomer(id).toString();
}
The Client Code
The client code for this example executes various HTTP GET requests to retrieve different representations of a Customer
. Each request sets the Accept
header a little differently so that it can obtain a different representation. For example:
src/test/java/com/restfully/shop/test/CustomerResourceTest.java
public
class
CustomerResourceTest
{
@Test
public
void
testCustomerResource()
throws
Exception
{
...
initialization
code
...
System.out.println("*** GET XML Created Customer **");
String
xml
=
client.target(location).request()
.accept(MediaType.APPLICATION_XML_TYPE)
.get(String.class);
System.out.println(xml);
System.out.println("*** GET JSON Created Customer **");
String
json
=
client.target(location).request()
.accept(MediaType.APPLICATION_JSON_TYPE)
.get(String.class);
System.out.println(json);
}
}
The SyncInvoker.accept()
method is used to initialize the Accept
header. The client extracts a String
from the HTTP response so it can show you the request XML or JSON.
Build and Run the Example Program
Perform the following steps:
1. Open a command prompt or shell terminal and change to the ex09_1 directory of the workbook example code.
2. Make sure your PATH is set up to include both the JDK and Maven, as described in Chapter 17.
3. Perform the build and run the example by typing maven install
.
Example ex09_2: Conneg via URL Patterns
Chapter 9 discussed how some clients, particularly browsers, are unable to use the Accept
header to request different formats from the same URL. To solve this problem, many JAX-RS implementations allow you to map a filename suffix (.html, .xml, .txt) to a particular media type. RESTEasy has this capability. We’re going to illustrate this using your browser as a client along with a slightly modified version of ex09_1.
The Server Code
A few minor things have changed on the server side. First, we add getCustomerHtml()
method to our CustomerResource
class:
@GET
@Path("{id}")
@Produces("text/html")
public
String
getCustomerHtml(@PathParam("id")
int
id)
{
return
"<h1>Customer As HTML</h1><pre>"
+
getCustomer(id).toString()
+
"</pre>";
}
Since you’re going to be interacting with this service through your browser, it might be nice if the example outputs HTML in addition to text, XML, and JSON.
The only other change to the server side is in the configuration for RESTEasy:
src/main/webapp/WEB-INF/web.xml
<web-app>
<context-param>
<param-name>
resteasy.media.type.mappings</param-name>
<param-value>
html : text/html,
txt : text/plain,
xml : application/xml
</param-value>
</context-param>
...
</web-app>
The resteasy.media.type.mappings
content parameter is added to define a mapping between various file suffixes and the media types they map to. A comma separates each entry. The suffix string makes up the first half of the entry and the colon character delimits the suffix from the media type it maps to. Here, we’ve defined mappings between .html and text/html
, .txt and text/plain
, and .xml and application/xml
.
Build and Run the Example Program
Perform the following steps:
1. Open a command prompt or shell terminal and change to the ex09_2 directory of the workbook example code.
2. Make sure your PATH is set up to include both the JDK and Maven, as described in Chapter 17.
3. Perform the build and run the example by typing maven jetty:run
.
The jetty:run
target will run the servlet container so that you can make browser invocations on it. Now, open up your browser and visit http://localhost:8080/customers/1.
Doing so will show you which default media type your browser requests. Each browser may be different. For me, Firefox 3.x prefers HTML, and Safari prefers XML.
Next, browse each of the following URLs: http://localhost:8080/customers/1.html, http://localhost:8080/customers/1.txt, and http://localhost:8080/customers/1.xml. You should see a different representation for each.