JBoss.orgCommunity Documentation
The two principal client side classes in Resteasy 2 are ClientRequest
and ClientResponse
:
ClientRequest request = new ClientRequest("http://localhost:8081/test"); request.body("text/plain", "hello world"); ClientResponse<?> response = request.post(); String result = response.getEntity(String.class);
ClientRequest
holds the target URL and entity, if any.
ClientResponse
holds the response entity, which can be
extracted by the getEntity() method.
In JAX-RS 2.0, these classes are replaced by four classes that support a fluent
call pattern: Client
, WebTarget
,
Invocation.Builder
, and Response
:
Client client = ClientBuilder.newClient(); WebTarget target = client.target("http://localhost:8081/test"); Invocation.Builder builder = target.request(); Entity<String> entity = Entity.entity("hello world", "text/plain"); Response response = builder.post(entity); String result = response.readEntity(String.class);
The invocation process begins with Client
, whose primary
responsibility is to create a WebTarget
. Client
s
are somewhat expensive to build, so it often makes sense to reuse a Client
to create multiple WebTarget
s.
Resteasy extends ClientBuilder
and Client
with methods that allow the registration of providers:
static class TestWriter implements MessageBodyWriter<String> { @Override public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return false; } @Override public long getSize(String t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return 0; } @Override public void writeTo(String t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { // } } ResteasyClientBuilder clientBuilder = new ResteasyClientBuilder(); Client client = clientBuilder.register(TestWriter.class).build();
All Client
s created by that ResteasyClientBuilder
,
and all invocations on all WebTarget
s created by those Client
s,
will have TestWriter
available.
WebTarget
, as its name implies, constructs and holds a URL which
targets a server side resource. It has various options for extending and manipulating
URIs:
WebTarget target = client.target("http://localhost:8081/test/{index}"); WebTarget target1 = target.resolveTemplate("index", "1"); WebTarget target2 = target.resolveTemplate("index", "2");
Here, two new WebTarget
s are created from the original target, each
with a different ending path segment. Query and matrix parameters can also be appended:
WebTarget target3 = target2.queryParam("x", "y");
Here, target3 targets "http://localhost:8081/test/2?x=y".
Resteasy also extends WebTarget
with the ability to register providers:
Client client = ClientBuilder.newClient(); String url = "http://localhost:8081/test/{index}"; WebTarget target = client.target(url).register(TestWriter.class); WebTarget target1 = target.resolveTemplate("index", "1"); WebTarget target2 = target.resolveTemplate("index", "2"); WebTarget target3 = target2.queryParam("x", "y");
Here, TestWriter
is available to all invocations on target1, target2, and target3.
Invocation.Builder
plays a role similar to the old ClientRequest
:
Response response = builder.header("User-Agent", "Mozilla/5.0").get();
or
String s = builder.header("User-Agent", "Mozilla/5.0").get(String.class);
Finally, note that Response
, unlike the old ClientResponse<T>
,
is not a generic type, so it is necessary to give a type when extracting a response entity:
String result = response.readEntity(String.class);
Note. Response.getEntity()
still exists, but it
plays a different role, which could easily lead to bugs. It is necessary to call
readEntity()
to extract the response entity. If
getEntity()
is called instead, it will return null.
Note. Unlike the old getEntity()
,
readEntity()
is not idempotent. Once it is called, the response is
closed, and subsequent calls will throw an IllegalStateException
. This
behavior can be circumvented by calling Response.bufferEntity()
before
calling readEntity()
. I.e., this will work:
response.bufferEntity(); System.out.println(response.readEntity(String.class)); System.out.println(response.readEntity(String.class));
The client framework in Resteasy 2 included a facility for interacting with JAX-RS resources through client side POJOs, not unlike the Java RMI facility:
@Path("/test") public static interface TestResource { @GET @Produces("text/plain") public String test(); } public void testProxy() throws Exception { String url = "http://localhost:8081"; TestResource pojo = ProxyFactory.create(TestResource.class, url); String result = pojo.test();
This technique avoids a lot of complications, but, perhaps because it is perceived as
not being in the RESTful spirit, it is not part of the JAX-RS 2.0 client framework. It
still exists in Resteasy 3, but in a re-worked form that fits into the official client
framework. Now, a proxy is created by a call to ResteasyWebTarget.proxy()
:
Client client = ClientBuilder.newClient(); String url = "http://localhost:8081"; ResteasyWebTarget target = (ResteasyWebTarget) client.target(url); TestResource pojo = target.proxy(TestResource.class); String result = pojo.test();
Resteasy 2 had two facilities for handling errors on the client side.
An instance of an org.jboss.resteasy.client.core.ClientErrorInterceptor
could be registered to handle exceptions thrown during a proxied call. Also, an instance of an
org.jboss.resteasy.client.exception.mapper.ClientExceptionMapper
could
be registered to map exceptions thrown during a proxied call. A default
ClientExceptionMapper
was installed that mapped exceptions thrown by
the HttpClient transport layer to Resteasy specific analogs. For example, an
org.apache.http.client.ClientProtocolException
would be mapped to an
org.jboss.resteasy.client.exception.ResteasyClientProtocolException
.
These two facilities do not exist in Resteasy 3. Instead, the JAX-RS 2.0 specification mandates the
use of javax.ws.rs.ProcessingException
and
javax.ws.rs.client.ResponseProcessingException
. In particular, exceptions
thrown while processing a request should be mapped to a ProcessingException
,
and exceptions thrown while processing a response should be mapped to a
ResponseProcessingException
.
For example, the ProcessingException
javadoc lists possible conditions
leading to a ProcessingException:
java.io.IOException
s thrown by
javax.ws.rs.ext.MessageBodyReader
s and
javax.ws.rs.ext.MessageBodyWriter
s
during entity serialization and de-serialization
Note that ProcessingException
and ResponseProcessingException
represent internal problems. If the client side receives a response with status codes 3xx, 4xx or 5xx,
it will map the response to an instance of javax.ws.rs.WebApplicationException
or one of its subclasses.