blah-blah
blah-blah
@javax.ws.rs.PathParam
@PathParam
allows you to inject the value of named URI path parameters that were defined in @Path
expressions:
@Path("/employee") public class EmployeeResource { @Path("{id}") @GET public Employee getEmployee(@PathParam("id") int id) { ... } }
In the example above, we want to route HTTP GET requests to the relative URI pattern /employee/{id}. The getEmployee(...)
method extracts an Employee ID from the URI using @PathParam
annotation. The value of the @PathParam
annotation,
"id
", matches the path parameter, {id}
, that was defined in the @Path
expression of
getEmployee(...)
.
While {id}
represents a string segment in the request's URI, JAX-RS runtime automatically converts the value to an int
before it invokes the getEmployee(...)
method. If the URI path parameter cannot be converted to an integer, the request is
considered a client error and the client will receive an HTTP 404, "Not Found" status code from the server.
@javax.ws.rs.QueryParam
The @QueryParam
annotation allows you to inject individual URI query parameters into your Java parameters. For example, let's say
you wanted to query a employee database and retrieve a subset of all employees in the database. Your URI might look like this:
GET /employees?start=30&size=10
The start
query parameter represents the employee index you want to start with and the size
query parameter represents
how many employees you want returned. The JAX-RS service that implemented this might look like this:
@Path("/employees") public class EmployeeResource { @GET @Produces("application/xml") public String getEmployees(@QueryParam("start") int start, @QueryParam("size") int size) { ... } }
The @QueryParam
annotation was used to inject the URI query parameters "start
" and "size
" into the Java
parameters start
and size
. As with other annotation injection, JAX-RS runtime automatically converts the query
parameter's string into an integer.
@javax.ws.rs.MatrixParam
The @MatrixParam
annotation extracts information from URL path segments.
@javax.ws.rs.HeaderParam
The @HeaderParam
annotation is used to inject HTTP request header values. For example, what if your
application was interested in the web page that referred to or linked to your web service? You could access the HTTP
Referer
header using the @HeaderParam
annotation:
@Path("/myresource") public class MyResource { @GET public String getData(@HeaderParam("Referer") String referer) { ... } }
The @HeaderParam
annotation is pulling the Referer
header directly from the HTTP
request and injecting it into the referer
method parameter.
A very important aspects of REST is hyperlinks, URIs, in representations that clients can use to transition the web service to new application states (this is otherwise known as "hypermedia as the engine of application state"). HTML forms present a good example of this in practice.
Using Java API for RESTful Web Services (JAX-RS), you can use the UriInfo
object to access request
headers. The UriInfo
object provides methods to enable you to find or build URI information of a
request.
Using an injected UriInfo
object by the JAX-RS runtime environment, the relative and absolute
uniform resource identifier (URI) information is known and available for modification. The
@javax.ws.rs.core.Context
annotation indicates that a context object is injected. The
javax.ws.rs.core.UriInfo
interface is the interface of the object that you want to inject. You
can use the UriInfo
object to build absolute and relative URLs using the UriBuilder
class.
If a resource method signature can be modified, add the @javax.ws.rs.core.Context
javax.ws.rs.core.UriInfo
parameter to the method. When the resource method is invoked, the
JAX-RS runtime environment passes an object that implements the UriInfo
object; for example:
@Path("/resource") public class RootResource { @GET public String getResource(@Context UriInfo uriInfo) { // Client used this URI to reach this resource method return uriInfo.getAbsolutePath().toASCIIString(); } }
If a resource method signature cannot be modified and the class is a root resource, add the
@javax.ws.rs.core.Context
javax.ws.rs.core.UriInfo
field. When the resource is
instantiated for a request, an object that implements UriInfo
is injected; for example:
@Path("/contextexample") public class RootResource { @Context UriInfo uriInfo; @GET public String getResource() { // Client used this URI to reach this resource method return uriInfo.getAbsolutePath().toASCIIString(); } }
Building URIs and building them safely is not easy with java.net.URI
, which is why JAX-RS has the
UriBuilder
class that makes it simple and easy to build URIs safely.
UriBuilder
can be used to build new URIs or build from existing URIs. For resource classes it is more
than likely that URIs will be built from the base URI the web service is deployed at or from the request URI.
The class UriInfo
provides such information (in addition to further information, see next section).
The following example shows URI building with UriInfo
and UriBuilder
:
@Path("/users/") public class UsersResource { @Context UriInfo uriInfo; ... @GET @Produces("application/json") public JSONArray getUsersAsJsonArray() { JSONArray uriArray = new JSONArray(); for (UserEntity userEntity : getUsers()) { UriBuilder ub = uriInfo.getAbsolutePathBuilder(); URI userUri = ub .path(userEntity.getUserid()) .build(); uriArray.put(userUri.toASCIIString()); } return uriArray; } }
An instance of UriInfo
can be injected into a class field or method parameter using the
@Context
annotation. UriInfo
provides both static and dynamic, per-request information,
about the components of a request URI. E.g. the following would return the names of any query parameters in a request:
@GET @Produces{"text/plain"} public String listQueryParamNames(@Context UriInfo info) { StringBuilder buf = new StringBuilder(); for (String param: info.getQueryParameters().keySet()) { buf.append(param); buf.append("\n"); } return buf.toString(); }
UriInfo
can be used to obtain URIs and associated UriBuilder
instances for the following
URIs: the base URI the application is deployed at; the request URI; and the absolute path URI, which is the
request URI minus any query components.
The getUsersAsJsonArray
method constructs a JSONArrray
where each element is a URI
identifying a specific user resource. The URI is built from the absolute path of the request URI by calling
uriInfo.getAbsolutePathBuilder()
. A new path segment is added, which is the user ID, and then the
URI is built. Notice that it is not necessary to worry about the inclusion of '/
' characters or that
the user ID may contain characters that need to be percent encoded. UriBuilder
takes care of such details.
UriBuilder
can be used to build/replace query or matrix parameters. URI templates can also be
declared, for example the following will build the URI:
http://localhost/segment?name=value
UriBuilder.fromUri("http://localhost/"). path("{a}"). queryParam("name", "{value}"). build("segment", "value");
The HTTP specification defines what HTTP response codes should be on a successful request. For example,
GET
should return 200, OK
and PUT
should return 201, CREATED
.
You can expect JAX-RS to return the same default response codes.
Sometimes, however, you need to specify your own response codes, or simply to add specific headers or cookies to
your HTTP response. JAX-RS provides a Response
class for this:
@Path("/orders")
public class OrderEntryService {
@GET
@Path("{id}")
public Response getOrder(@PathParm("id") int orderId) {
Order order = OrderDAO.get(orderId);
ResponseBuilder builder = Response.ok(order);
Date expirationDate = new Date(System.currentTimeMillis() + 3000); // 3 secs
builder.expires(expirationDate);
return builder.build();
}
}
In this example, we still want to return a JAXB object with a 200 status code, but we want to add an
Expires
header to the response. You use the ResponseBuilder
class to build up the response,
and ResponseBuilder.build()
to create the final Response
instance.
Java API for RESTful Web Services (JAX-RS) enables developers to add a custom entity provider to the application. Use custom entity providers when you want to use Java types to represent incoming request message bodies as well as represent outgoing response message bodies. By adding a custom entity provider, you can deserialize custom Java types from message bodies and serialize any media type as message bodies.
A custom entity provider is created by annotating a class with a javax.ws.rs.ext.Provider
annotation. The
class must implement the javax.ws.rs.ext.MessageBodyReader
interface or the
javax.ws.rs.ext.MessageBodyWriter
interface, or both. You must add the provider class to the list of
classes returned in the javax.ws.rs.core.Application
subclass getClasses()
method.
The MessageBodyReader
interface represents a contract for a provider that supports the conversion of a
stream to a Java type. To add a MessageBodyReader
implementation, annotate the implementation class with
@Provider
. A MessageBodyReader
implementation may be annotated with @Consumes
to restrict the media types for which it will be considered suitable:
package javax.ws.rs.ext; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; public interface MessageBodyReader<T extends Object> { public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType); public T readFrom(Class<T> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException; }
The MessageBodyWriter
interface represents a contract for a provider that supports the conversion of
a Java type to a stream. To add a MessageBodyWriter
implementation, annotate the implementation class
with @Provider
. A MessageBodyWriter
implementation may be annotated with
Produces
to restrict the media types for which it will be considered suitable:
package javax.ws.rs.ext; import java.io.IOException; import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; public interface MessageBodyWriter<T extends Object> { public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType); public long getSize(T t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType); public void writeTo(T t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException; }
Create a new Java class that is your custom entity provider. In this procedure, the example code creates
a reader and a writer for by.boot.java.Order
types so that you can use the
by.boot.java.Order
type as an incoming request entity parameter and as a return type to
contain the response entity:
public class OrderEntityProvider { }
Add the @javax.ws.rs.ext.Provider
annotation. Adding this annotation indicates to the
JAX-RS runtime environment that this class is a JAX-RS provider. If this Provider
annotation
is not specified, the runtime environment does not detect that the class is a custom provider:
@javax.ws.rs.ext.Provider public class OrderEntityProvider { }
[optional] Add a @javax.ws.rs.Consumes
and/or @javax.ws.rs.Produces
annotation if
you want to limit the media types that the entity provider supports. In the following code snippet, the
provider is only invoked when the incoming Content-Type
or outgoing
Content-Type
is application/json
:
@javax.ws.rs.ext.Provider @javax.ws.rs.Consumes("application/json") @javax.ws.rs.Produces("application/json") public class OrderEntityProvider { }
Implement javax.ws.rs.ext.MessageBodyReader<T>
if the entity provider needs to
deserialize a message body.
You can use the generic type <T>
to limit the types supported by the entity provider.
By defining the message body reader as
javax.ws.rs.ext.MessageBodyReader<by.boot.java.Order>
, the JAX-RS runtime environment
knows that only by.boot.java.Order
objects can be produced:
@javax.ws.rs.ext.Provider @javax.ws.rs.Consumes("application/json") @javax.ws.rs.Produces("application/json") public class OrderEntityProvider implements MessageBodyReader<by.boot.java.Order> { public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return by.boot.java.Order.class == type; } public by.boot.java.Order readFrom(Class<by.boot.java.Order> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { // This code reads from the entityStream and constructs the object. by.boot.java.Order retObj = by.boot.java.Order.parse(entityStream); return retObj; } }
If an entity provider needs to support a complex set of types, consider implementing
javax.ws.rs.ext.MessageBodyReader<Object>
.
Implement javax.ws.rs.ext.MessageBodyWriter<T>
if the entity provider needs to
serialize a message body. You can implement the MessageBodyReader<T>
and
MessageBodyWriter<T>
interfaces in the same Java class. In the following code
snippet, only the MessageBodyWriter
implementation is shown:
@javax.ws.rs.ext.Provider public class OrderEntityProvider implements MessageBodyWriter<by.boot.java.Order> { public long getSize(by.boot.java.Order t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { // Return -1 if the content length cannot be determined return -1; } public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return by.boot.java.Order.class == type; } public void writeTo(by.boot.java.Order t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { entityStream.write(t.getBytes()); } }
Add the custom entity provider to the javax.ws.rs.core.Application
subclass and add the
provider to the set of classes returned from the getClasses()
method. You can add multiple
custom entity providers to the set of classes returned:
public class MyApplication extends javax.ws.rs.core.Application { public Set<Class<?>> getClasses() { Set<Class<?>> classes = new HashSet<Class<?>>(); classes.add(OrderEntityProvider.class); return classes; } }
JAX-RS has a RuntimeException
class, WebApplicationException
, that allows you to abort
your JAX-RS service method. It can take an HTTP status code or even a Response
object as one of its
constructor parameters. For example:
@Path("/orders") public class OrderEntryService { @GET @Path("{id}") @Produces("application/xml") public Order getOrder(@PathParm("id") int orderId) { Order order = OrderDAO.get(orderId); if (order == null) { ResponseBuilder builder = Response.status(Status.NOT_FOUND); builder.type("text/html"); builder.entity("<h3>Order Not Found</h3>"); throw new WebApplicationException(builder.build(); } return order; } }
In this example, if the order is null
, send a HTTP response code of NOT_FOUND
with a HTML
encoded error message.
Beyond WebApplicationException
, you can map non-JAX-RS exceptions that might be thrown by your
application to a Response
object by registering implementations of the ExceptionMapper
class:
public interface ExceptionMapper<E extends Throwable> { Response toResponse(E exception); }
For example, lets say we were using JPA to locate our Order
objects. We could map
javax.persistence.EntityNotFoundException
to return a NOT_FOUND
status code:
@Provider public class EntityNotFoundMapper implements ExceptionMapper<EntityNotFoundException> { Response toResponse(EntityNotFoundException exception) { return Response.status(Status.NOT_FOUND); } }
or
@Provider public class EntityNotFoundMapper implements ExceptionMapper<EntityNotFoundException> { Response toResponse(EntityNotFoundException exception) { return Response.status(Status.NOT_FOUND). entity(exception.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 should 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.
Methods of a resource class that are annotated with @Path
are either sub-resource methods
or sub-resource locators. Sub-resource methods handle an HTTP request directly whilst sub-resource locators
return an object that will handle an HTTP request. The presence or absence of a request method designator (e.g. @GET
)
differentiates between the two:
Request method designator (@GET
, @POST
) presents:
Such methods, known as sub-resource methods, are treated like a normal resource method except the method is only invoked for request URIs that match a URI template created by concatenating the URI template of the resource class with the URI template of the method.
Example:
@Path("widgets") public class WidgetsResource { @GET @Path("offers") public WidgetList getDiscounted() {...} }
In the above a GET
request for the widgets/offers
resource is handled directly by the getDiscounted()
sub-resource method of the resource class WidgetsResource
.
Request method designator (@GET
, @POST
) absent:
Such methods, known as sub-resource locators, are used to dynamically resolve the object that will handle the request. Any returned object is treated as a resource class instance and used to either handle the request or to further resolve the object that will handle the request. An implementation MUST dynamically determine the class of object returned rather than relying on the static sub-resource locator return type since the returned instance may be a subclass of the declared type with potentially different annotations. Sub-resource locators may have all the same parameters as a normal resource method except that they MUST NOT have an entity parameter.
Example:
@Path("widgets") public class WidgetsResource { @Path("{id}") public WidgetSubResource findWidget(@PathParam("id") String id) { return new WidgetSubResource(id); } }
public class WidgetSubResource { public WidgetSubResource(String id) {...} @GET public Widget getDetails() {...} }
In the above a GET
request for widgets/xxx
is handled by the getDetails()
method of the
WidgetSubResource
resource class.
blah-blah
![]() |
![]() ![]() |