1. Trang chủ >
  2. Công Nghệ Thông Tin >
  3. Kỹ thuật lập trình >

What’s New in JAX-RS 2.0?

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (15.81 MB, 597 trang )


Chapter 15 ■ RESTful Web Services



Table 15-4.  Main JAX-RS Packages



Package



Description



javax.ws.rs



High-level interfaces and annotations used to create RESTful web service



javax.ws.rs.client



Classes and interfaces of the new JAX-RS client API



javax.ws.rs.container



Container-specific JAX-RS API



javax.ws.rs.core



Low-level interfaces and annotations used to create RESTful web resources



javax.ws.rs.ext



APIs that provide extensions to the types supported by the JAX-RS API



Reference Implementation

Jersey is the reference implementation of JAX-RS. It is an open source project under dual CDDL and GPL licenses.

Jersey also provides a specific API so that you can extend Jersey itself.

Other implementations of JAX-RS are also available such as CXF (Apache), RESTEasy (JBoss), and Restlet

(a senior project that existed even before JAX-RS was finalized).



Writing RESTful Web Services

Some of the low-level concepts (such as the HTTP protocol) might have you wondering how the code would look

when developing a RESTful web service. The good news is that you don’t have to write plumbing code to digest HTTP

requests, nor create HTTP responses by hand. JAX-RS is a very elegant API allowing you to describe a RESTful web

service with only a few annotations. RESTful web services are POJOs that have at least one method annotated with @

javax.ws.rs.Path. Listing 15-3 shows a typical resource.

Listing 15-3.  A Simple Book RESTful Web Service

@Path("/book")

public class BookRestService {



@GET

@Produces("text/plain")

public String getBookTitle() {

return "H2G2";

}

}



The BookRestService is a Java class annotated with @Path, indicating that the resource will be hosted at the

URI path /book. The getBookTitle() method is marked to process HTTP GET requests (using @GET annotation)

and produces text (the content is identified by the MIME Media text/plain; I could have also used the constant

MediaType.TEXT_PLAIN). To access this resource, you need an HTTP client such as a browser to point to the URL

http://www.myserver.com/book.

JAX-RS is HTTP-centric by nature and has a set of clearly defined classes and annotations to deal with HTTP

and URIs. A resource can have several representations, so the API provides support for a variety of content types and

uses JAXB to marshall and unmarshall XML representations from/into objects. JAX-RS is also independent of the

container, so resources can be deployed in GlassFish, of course, but also in a variety of servlet containers.



508

www.it-ebooks.info



Chapter 15 ■ RESTful Web Services



Anatomy of a RESTful Web Service

From Listing 15-3, you can see that the REST service doesn’t implement any interface nor extend any class; the only

mandatory annotation to turn a POJO into a REST service is @Path. JAX-RS relies on configuration by exception, so it

has a set of annotations to configure the default behavior. Following are the requirements to write a REST service:





The class must be annotated with @javax.ws.rs.Path (in JAX-RS 2.0 there is no XML

equivalent to meta-data as there is no deployment descriptor).







The class must be defined as public, and it must not be final or abstract.







Root resource classes (classes with a @Path annotation) must have a default public constructor.

Non-root resource classes do not require such a constructor.







The class must not define the finalize() method.







To add EJB capabilities to a REST service, the class has to be annotated with

@javax.ejb.Stateless or @javax.ejb.Singleton (see Chapter 7).







A service must be a stateless object and should not save client-specific state across method calls.



CRUD Operations on a RESTful Web Service

Listing 15-3 shows how to write a very simple REST service that returns a String. But most of the time you need to

access a database, retrieve or store data in a transactional manner. For this you can have a REST service and add

stateless session beans functionalities by adding the @Stateless annotation. This will allow transactional access to a

persistent layer (JPA entities), as shown in Listing 15-4.

Listing 15-4.  A Book RESTful Web Service Creating, Deleting, and Retrieving Books from the Database

@Path("book")

@Stateless

public class BookRestService {



@Context

private UriInfo uriInfo;

@PersistenceContext(unitName = "chapter15PU")

private EntityManager em;



@GET

@Produces(MediaType.APPLICATION_XML)

public Books getBooks() {

TypedQuery query = em.createNamedQuery(Book.FIND_ALL, Book.class);

Books books = new Books(query.getResultList());

return books;

}



@POST

@Consumes(MediaType.APPLICATION_XML)

public Response createBook(Book book) {

em.persist(book);

URI bookUri = uriInfo.getAbsolutePathBuilder().path(book.getId().toString()).build();

return Response.created(bookUri).build();

}





509

www.it-ebooks.info



Chapter 15 ■ RESTful Web Services



@DELETE

@Path("{id}")

public Response deleteBook(@PathParam("id") Long bookId) {

em.remove(em.find(Book.class, bookId));

return Response.noContent().build();

}

}



The code in Listing 15-4 represents a REST service that can consume and produce an XML representation of

a book. The getBooks() method retrieves the list of books from the database and returns an XML representation

(using content negotiation) of this list, accessible through a GET method. The createBook() method takes an XML

representation of a book and persists it to the database. This method is invoked with an HTTP POST and returns a

Response with the URI (bookUri) of the new book as well as the created status. The deleteBook method takes a book

id as a parameter and deletes it from the database.

The code in Listing 15-4 follows a very simple JAX-RS model and uses a set of powerful annotations. Let’s now

take a deeper look at all the concepts shown in the code.



URI Definition and Binding URIs

The @Path annotation represents a relative URI that can annotate a class or a method. When used on classes, it is

referred to as the root resource, providing the root of the resource tree and giving access to subresources. Listing 15-5

shows a REST service that can be access at http://www.myserver.com/items. All the methods of this service will have

/items as root.

Listing 15-5.  Root Path to an Item Resource

@Path("/items")

public class ItemRestService {



@GET

public Items getItems() {

// ...

}

}



You can then add subpaths to your methods, which can be useful to group together common functionalities for

several resources as shown in Listing 15-6 (you may ignore for the moment the @GET, @POST, and @DELETE annotations

in the listing, as they will be described later in the “HTTP Method Matching” section).

Listing 15-6.  Several Subpaths in the ItemRestService

@Path("/items")

public class ItemRestService {



@GET

public Items getItems() {

// URI : /items

}





510

www.it-ebooks.info



s



@GET

@Path("/cds")

public CDs getCDs() {

// URI : /items/cds

}

@GET

@Path("/books")

public Books getBooks() {

// URI : /items/books

}

@POST

@Path("/book")

public Response createBook(Book book) {

// URI : /items/book

}

}

Listing 15-6 represents a RESTful web service that will give you methods to get all the items (CDs and books) from

the CD-BookStore Application. When requesting the root resource /items, the only method without sub @Path will

be selected (getItems()). Then, when @Path exists on both the class and method, the relative path to the method is a

concatenation of both. For example, to get all the CDs, the path will be /items/cds. When requesting /items/books,

the getBooks() method will be invoked. To create a new book you need to point at /items/book.

If @Path("/items") only existed on the class, and not on any methods, the path to access each method would

be the same. The only way to differentiate them would be the HTTP verb (GET, PUT, etc.) and the content negotiation

(text, XML, etc.), as you’ll later see.



Extracting Parameters

Having nice URIs by concatenating paths to access your resource is very important in REST. But paths and subpaths

are not enough: you also need to pass parameters to your RESTful web services, extract and process them at runtime.

Listing 15-4 showed how to get a parameter out of the path with @javax.ws.rs.PathParam. JAX-RS provides a rich set

of annotations to extract the different parameters that a request could send (@PathParam, @QueryParam, @MatrixParam,

@CookieParam, @HeaderParam, and @FormParam).

Listing 15-7 shows how the @PathParam annotation is used to extract the value of a URI template parameter.

A parameter has a name and is represented by a variable between curly braces or by a variable that follows a regular

expression. The searchCustomers method takes any String parameter while getCustomerByLogin only allows

lowercase/uppercase alphabetical letters ([a-zA-Z]*) and getCustomerById only digits (\\d+).

Listing 15-7. Extracting Path Parameters and Regular Expressions

@Path("/customer")

@Produces(MediaType.APPLICATION_JSON)

public class CustomerRestService {

@Path("search/{text}")

public Customers searchCustomers(@PathParam("text") String textToSearch) {

// URI : /customer/search/smith

}



511

www.it-ebooks.info



Chapter 15 ■ RESTful Web Services



@GET

@Path("{login: [a-zA-Z]*}")

public Customer getCustomerByLogin(@PathParam("login") String login) {

// URI : /customer/foobarsmith

}



@GET

@Path("{customerId : \\d+}")

public Customer getCustomerById(@PathParam("customerId") Long id) {

// URI : /customer/12345

}

}



The @QueryParam annotation extracts the value of a URI query parameter. Query parameters are key/value pairs

separated by an & symbol such as http://www.myserver.com/customer?zip=75012&city=Paris. The @MatrixParam

annotation acts like @QueryParam, except it extracts the value of a URI matrix parameter (; is used as a delimiter

instead of ?). Listing 15-8 shows how to extract both query and matrix parameters from URIs.

Listing 15-8.  Extracting Query and Matrix Parameters

@Path("/customer")

@Produces(MediaType.APPLICATION_JSON)

public class CustomerRestService {



@GET

public Customers getCustomersByZipCode(@QueryParam("zip") Long zip, 

@QueryParam("city") String city) {

// URI : /customer?zip=75012&city=Paris

}



@GET

@Path("search")

public Customers getCustomersByName(@MatrixParam("firstname") String firstname, 

@MatrixParam("surname") String surname) {

// URI : /customer/search;firstname=Antonio;surname=Goncalves

}

}



Two other annotations are related to the innards of HTTP, things you don’t see directly in URIs: cookies and

HTTP headers @CookieParam extracts the value of a cookie, while @HeaderParam extracts the value of a header field.

Listing 15-9 extracts the session ID from the cookie and the User Agent from the HTTP header.

Listing 15-9.  Extracting Values From the Cookie and HTTP Header

@Path("/customer")

@Produces(MediaType.TEXT_PLAIN)

public class CustomerRestService {



@GET

public String extractSessionID(@CookieParam("sessionID") String sessionID) {

// ...

}





512

www.it-ebooks.info



Chapter 15 ■ RESTful Web Services



@GET

public String extractUserAgent(@HeaderParam("User-Agent") String userAgent) {

// ...

}

}



The @FormParam annotation specifies that the value of a parameter is to be extracted from a form in a request

entity body. @FormParam is not required to be supported on fields or properties.

With all these annotations, you can add a @DefaultValue annotation to define the default value for a parameter

you’re expecting. The default value is used if the corresponding parameter is not present in the request. Listing 15-10

sets default values to query and matrix parameters. For example, in the method getCustomersByAge, if the query

parameter age is not in the request, the default value is set to 50.

Listing 15-10.  Defining Default Values

@Path("/customer")

public class CustomerRestService {



@GET

public Customers getCustomersByAge(@DefaultValue("50") @QueryParam("age") int age) {

// ...

}



@GET

public Customers getCustomersByCity(@DefaultValue("Paris") @MatrixParam("city") 

String city) {

// ...

}

} 



Consuming and Producing Content Types

With REST, the same resource can have several representations; a book can be represented as a web page, a PDF,

or an image showing the book cover. JAX-RS specifies a number of Java types that can represent a resource such as

String, InputStream and JAXB beans. The @javax.ws.rs.Consumes and @javax.ws.rs.Produces annotations may

be applied to a resource where several representations are possible. It defines the media types of the representation

exchanged between the client and the server. JAX-RS has a javax.ws.rs.core.MediaType class that acts like an

abstraction for a MIME type. It has several methods and defines the constants listed in Table 15-5.



513

www.it-ebooks.info



Chapter 15 ■ RESTful Web Services



Table 15-5.  MIME Types Defined in MediaType



Constant name



MIME type



APPLICATION_ATOM_XML



“application/atom+xml”



APPLICATION_FORM_URLENCODED



“application/x-www-form-urlencoded”



APPLICATION_JSON



“application/json”



APPLICATION_OCTET_STREAM



“application/octet-stream”



APPLICATION_SVG_XML



“application/svg+xml”



APPLICATION_XHTML_XML



“application/xhtml+xml”



APPLICATION_XML



“application/xml”



MULTIPART_FORM_DATA



“multipart/form-data”



TEXT_HTML



“text/html”



TEXT_PLAIN



“text/plain”



TEXT_XML



“text/xml”



WILDCARD



“*/*”



Using the @Consumes and @Produces annotations on a method overrides any annotations on the resource class for

a method argument or return type. In the absence of either of these annotations, support for any media type (*/*) is

assumed. By default, CustomerRestService produces plain text representations that are overridden in some methods

(see Listing 15-11). Note that the getAsJsonAndXML produces an array of representations (XML or JSON).

Listing 15-11.  A Customer Resource with Several Representations

@Path("/customer")

@Produces(MediaType.TEXT_PLAIN)

public class CustomerRestService {



@GET

public Response getAsPlainText() {

// ...

}



@GET

@Produces(MediaType.TEXT_HTML)

public Response getAsHtml() {

// ...

}



@GET

@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})

public Response getAsJsonAndXML() {

// ...

}





514

www.it-ebooks.info



Chapter 15 ■ RESTful Web Services



@PUT

@Consumes(MediaType.TEXT_PLAIN)

public void putName(String customer) {

// ...

}

}



If a RESTful web service is capable of producing more than one media type, the targeted method will correspond

to the most acceptable media type, as declared by the client in the Accept header of the HTTP request. For example,

if the Accept header is:



Accept: text/plain



and the URI is /customer, the getAsPlainText() method will be invoked. But the client could have used the

following HTTP header:



Accept: text/plain; q=0.8, text/html



This header declares that the client can accept media types of text/plain and text/html but prefers the latter

using the quality factor (or preference weight) of 0.8 (“I prefer text/html, but send me text/plain if it is the best

available after an 80% markdown in quality”). By including such header and pointing at the /customer URI, the

getAsHtml() method will be invoked.



Returned Types

So far you’ve seen mostly how to invoke a method (using parameters, media type, HTTP methods . . .) without caring

about the returned type. What can a RESTful web service return? Like any Java class, a method can return any

standard Java type, a JAXB bean or any other object as long as it has a textual representation that can be transported

over HTTP. In this case, the runtime determines the MIME type of the object being returned and invokes the

appropriate Entity Provider (see later) to get its representation. The runtime also determines the appropriate HTTP

return code to send to the consumer (204-No Content if the resource method's return type is void or null; 200-OK if

the returned value is not null). But sometimes you want finer control of what you are returning: the response body

(a.k.a. an entity) of course, but also the response code and/or response headers or cookies. That’s when you return a

Reponse object. It is a good practice to return a javax.ws.rs.core.Response with an entity since it would guarantee a

return content type. Listing 15-12 shows you different return types.

Listing 15-12.  A Customer Service Returning Data Types, a JAXB Bean, and a Response

@Path("/customer")

public class CustomerRestService {



@GET

public String getAsPlainText() {

return new Customer("John", "Smith", "jsmith@gmail.com", "1234565").toString();

}



@GET

@Path("maxbonus")

public Long getMaximumBonusAllowed() {

return 1234L;

}





515

www.it-ebooks.info



Chapter 15 ■ RESTful Web Services



@GET

@Produces(MediaType.APPLICATION_XML)

public Customer getAsXML() {

return new Customer("John", "Smith", "jsmith@gmail.com", "1234565");

}



@GET

@Produces(MediaType.APPLICATION_JSON)

public Response getAsJson() {

return Response.ok(new Customer("John", "Smith", "jsmith@gmail.com", "1234565"),

MediaType.APPLICATION_JSON).build();

}

}



The getAsPlainText method returns a String representation of a customer and the getMaximumBonusAllowed

returns a numerical constant. The defaults will apply so the return HTTP status on both methods will be 200-OK (if no

exception occurs). The getAsXML returns a Customer JAXB POJO meaning that the runtime will marshall the object

into an XML representation.

The getAsJson method doesn’t return an entity but instead a javax.ws.rs.core.Response object. A Response

wraps the entity that is returned to the consumer and it’s instantiated using the ResponseBuilder class as a factory.

In this example, we still want to return a JAXB object (the Customer) with a 200-OK status code (the ok() method), but

we also want to specify the MIME type to be JSON. Calling the ResponseBuilder.build() method creates the final

Response instance.

It is recommended to return a custom Response for all requests rather than the entity itself (you can then set a

specific status code if needed). Table 15-6 shows a subset of the Response API.

Table 15-6.  The Response API



Method



Description



accepted()



Creates a new ResponseBuilder with an accepted status



created()



Creates a new ResponseBuilder for a created resource (with its URI)



noContent()



Creates a new ResponseBuilder for an empty response



notModified()



Creates a new ResponseBuilder with a not-modified status



ok()



Creates a new ResponseBuilder with an ok status



serverError()



Creates a new ResponseBuilder with an server error status



status()



Creates a new ResponseBuilder with the supplied status



temporaryRedirect()



Creates a new ResponseBuilder for a temporary redirection



getCookies()



Gets the cookies from the response message



getHeaders()



Gets the headers from the response message



getLinks()



Get the links attached to the message as header



getStatus()



Get the status code associated with the response



readEntity()



Read the message entity as an instance of specified Java type using a

MessageBodyReader that supports mapping the message onto the requested type



516

www.it-ebooks.info



Chapter 15 ■ RESTful Web Services



The Response and ResponseBuilder follow the fluent API design pattern. Meaning you can easily write a

response by concatenating methods. This also makes the code more readable. Here are some examples of what you

can write with this API:



Response.ok().build();

Response.ok().cookie(new NewCookie("SessionID", "5G79GDIFY09")).build();

Response.ok("Plain Text").expires(new Date()).build();

Response.ok(new Customer ("John", "Smith"), MediaType.APPLICATION_JSON).build();

Response.noContent().build();

Response.accepted(new Customer("John", "Smith", "jsmith@gmail.com", "1234565")).build();

Response.notModified().header("User-Agent", "Mozilla").build(); 



HTTP Method Matching

You’ve seen how the HTTP protocol works with its requests, responses, and action methods (GET, POST, PUT, etc.). JAXRS defines these common HTTP methods using annotations: @GET, @POST, @PUT, @DELETE, @HEAD, and @OPTIONS. Only

public methods may be exposed as resource methods. Listing 15-13 shows a customer RESTful web service exposing

CRUD methods: @GET methods to retrieve resources, @POST methods to create a new resource, @PUT methods to update

an existing resource, and @DELETE methods to delete a resource.

Listing 15-13.  A Customer Resource Exposing CRUD Operations and Retuning Responses

@Path("/customer")

@Produces(MediaType.APPLICATION_XML)

@Consumes(MediaType.APPLICATION_XML)

public class CustomerRestService {



@GET

public Response getCustomers() {

// ..

return Response.ok(customers).build();

}



@GET

@Path("{customerId}")

public Response getCustomer(@PathParam("customerId") String customerId) {

// ..

return Response.ok(customer).build();

}



@POST

public Response createCustomer(Customer customer) {

// ..

return Response.created(createdCustomerURI).build();

}



@PUT

public Response updateCustomer(Customer customer) {

// ..

return Response.ok(customer).build();

}





517

www.it-ebooks.info



Chapter 15 ■ RESTful Web Services



@DELETE

@Path("{customerId}")

public Response deleteCustomer(@PathParam("customerId") String customerId) {

// ..

return Response.noContent().build();

}

}



The HTTP specification defines what HTTP response codes should be on a successful request. You can expect

JAX-RS to return the same default response codes:





GET methods retrieve whatever information (in the form of an entity) is identified by the

requested URI. GET should return 200-OK.







The PUT method refers to an already existing resource that needs to be updated. If an existing

resource is modified, either the 200-OK or 204-No Content response should be sent to indicate

successful completion of the request.







The POST method is used to create a new resource identified by the request URI. The response

should return 201-CREATED with the URI of this new resource or 204-No Content if it does not

result in a resource that can be identified by a URI.







The DELETE method requests that the server deletes the resource identified by the requested

URI. A successful response should be 200-OK if the response includes an entity, 202-Accepted

if the action has not yet been enacted, or 204-No Content if the action has been enacted but

the response does not include an entity.



Building URIs

Hyperlinks are a central aspect of REST applications. In order to evolve through the application states, RESTful web

services need to be agile at managing transition and building URIs. JAX-RS provides a javax.ws.rs.core.UriBuilder

that aims at replacing java.net.URI for making it easier to build URIs in a safe manner. UriBuilder has a set of

methods that can be used to build new URIs or build from existing URIs. Listing 15-14 gives you some examples of

how you can use the UriBuilder to create any kind of URI with path, query, or matrix parameters.

Listing 15-14.  Using UriBuilder

public class URIBuilderTest {



@Test

public void shouldBuildURIs() {

URI uri = 

UriBuilder.fromUri("http://www.myserver.com").path("book").path("1234").build();

assertEquals("http://www.myserver.com/book/1234", uri.toString());



uri = UriBuilder.fromUri("http://www.myserver.com").path("book") 

.queryParam("author", "Goncalves").build();

assertEquals("http://www.myserver.com/book?author=Goncalves", uri.toString());



uri = UriBuilder.fromUri("http://www.myserver.com").path("book") 

.matrixParam("author", "Goncalves").build();

assertEquals("http://www.myserver.com/book;author=Goncalves", uri.toString());





518

www.it-ebooks.info



Xem Thêm
Tải bản đầy đủ (.pdf) (597 trang)

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×