juneau-rest-client-classic
juneau-rest-client (org.apache.juneau.rest.client.RestClient) is the recommended REST client. It is generally
available in 10.0.0, transport-agnostic, and uses the JDK HttpClient by default. juneau-rest-client-classic
(org.apache.juneau.rest.client.classic.RestClient, Apache HttpClient 4.5) stays supported and is not deprecated; it is
the stable choice for existing users. See Choosing a REST Client.
This page documents the classic client, which ships in the juneau-rest-client-classic module as of 10.0.0.
The recommended juneau-rest-client artifact is described in Next-Generation REST Client.
| Maven | |
|---|---|
| Jar | juneau-rest-client-classic-10.0.0.jar |
| OSGi | org.apache.juneau.rest.client.classic_10.0.0.jar |
| Links | Maven Central · Javadocs · Source |
Contents/Features
Built upon the feature-rich Apache HttpClient library, the Juneau RestClient API adds support for fluent-style REST calls and the ability to perform marshalling of POJOs to and from HTTP parts.
// Create a basic REST client with JSON support and download a bean.
MyBean bean = RestClient.create()
.json5()
.build()
.get(URI)
.run()
.assertStatus().asCode().is(200)
.assertHeader("Content-Type").matchesSimple("application/json*")
.getContent().as(MyBean.class);
Breaking apart the fluent call, we can see the classes being used:
RestClient.Builder builder = RestClient.create().json5();
RestClient client = builder.build();
RestRequest req = client.get(URI);
RestResponse res = req.run();
RestResponseStatusLineAssertion statusLineAssertion = res.assertStatus();
FluentIntegerAssertion codeAssertion = statusLineAssertion.asCode();
res = codeAssertion.is(200);
FluentStringAssertion headerAssertion = res.assertHeader("Content-Type");
res = headerAssertion.matchesSimple("application/json*");
ResponseContent content = res.getContent();
MyBean bean = content.as(MyBean.class);
It additionally provides support for creating remote proxy interfaces using REST as the transport medium.
// Define a Remote proxy for interacting with a REST interface.
@Remote(path="/petstore")
public interface PetStore {
@RemotePost("/pets")
Pet addPet(
@Content CreatePet pet,
@Header("E-Tag") UUID etag,
@Query("debug") boolean debug
);
}
PetStore store = RestClient
.create()
.json5()
.build()
.getRemote(PetStore.class, "http://localhost:10000");
CreatePet createPet = new CreatePet("Fluffy", 9.99);
Pet pet = store.addPet(createPet, UUID.randomUUID(), true);
The classes are closely tied to Apache HttpClient, yet provide lots of additional functionality:
Instances of this class are built using the RestClient.Builder class which can be constructed using the RestClient.create() method as shown above.
Clients are typically created with a root URI so that relative URIs can be used when making requests.
This is done using the RestClient.Builder.rootUrl(Object) method.
// Create a client where all URIs are relative to localhost.
RestClient client = RestClient.create().json().rootUrl("http://localhost:10000").build();
// Use relative paths.
String content = client.get("/subpath").run().getContent().asString();
The root URL can also be inspected after the client is built using RestClient.getRootUrl().
String rootUrl = client.getRootUrl(); // "http://localhost:10000"
Dynamic Root URL
The RestClient.Builder.rootUrl(Supplier<String>) overload allows the root URL to be computed dynamically at request time. The supplier is called on every request, so the URL can change between requests without rebuilding the client.
Three common patterns are supported:
Swappable reference — use an AtomicReference when you need to point the client at a different host at any time:
var urlRef = new AtomicReference<>("https://host1");
RestClient client = RestClient.create().json().rootUrl(urlRef::get).build();
// Later, switch to a different host without rebuilding the client.
urlRef.set("https://host2");
Refreshable cached value — use Memoizer (via Suppliers.memoizer()) when computing the URL is expensive (e.g. service discovery) and you want it cached until explicitly refreshed:
// URL is fetched from service discovery once, then cached.
Memoizer<String> url = Suppliers.memoizer(() -> serviceDiscovery.findUrl("my-service"));
RestClient client = RestClient.create().json().rootUrl(url).build();
// After a failover, force re-evaluation on the next request.
url.reset();
Purely dynamic — use a plain lambda when the URL must be re-evaluated on every request:
// URL is read from a live config source on every request.
RestClient client = RestClient.create().json().rootUrl(() -> config.getRootUrl()).build();
The RestClient class creates RestRequest objects using the following methods:
The RestRequest class creates RestResponse objects using the following methods:
The distinction between the two methods is that complete() automatically consumes the response body and run() does not.
Note that you must consume response bodies in order for HTTP connections to be freed up for reuse!
The InputStreams returned by the ResponseContent object are auto-closing once they are exhausted, so it is often not necessary to explicitly close them.
The following examples show the distinction between the two calls:
// Consuming the response, so use run().
String content = client.get(URI).run().getContent().asString();
// Only interested in response status code, so use complete().
int status = client.get(URI).complete().getStatusCode();
Resource Management with Try-With-Resources
Both RestRequest and RestResponse implement AutoCloseable and can be used in try-with-resources blocks for automatic resource cleanup.
Use try-with-resources to ensure proper cleanup of HTTP connections:
// RestRequest will automatically close the response when the try block exits
try (RestRequest req = client.get(URI)) {
String content = req.run().getContent().asString();
}
// Or explicitly manage both request and response
try (RestRequest req = client.get(URI);
RestResponse res = req.run()) {
String content = res.getContent().asString();
}
The close() methods follow these rules:
- Unchecked exceptions (
RuntimeExceptionandError) are allowed to propagate for debuggability - Checked exceptions are caught and logged but not thrown, following AutoCloseable best practices
This design prevents close exceptions from masking the original exception while still making programming errors visible during development.
REST Client Stacks
Juneau ships two REST client stacks:
- The classic
org.apache.juneau.rest.client.classic.RestClient(HC4.5-based, supports the rich fluent surface shown above) in thejuneau-rest-client-classicMaven module. - The next-generation
org.apache.juneau.rest.client.RestClient(transport-agnostic, no Apache HttpClient dependency) in thejuneau-rest-clientMaven module — see Next-Generation REST Client.
Both clients share the same fluent style for marshalling POJOs to and from HTTP parts.
REST Testing Framework
The MockRestClient class is used for performing serverless unit testing of @Rest-annotated and @Remote-annotated classes. It perform full serialization and parsing of the HTTP request and responses, but bypasses the network layer to significantly improve speed while still performing real testing.
public class MockTest {
// A simple bean with one field.
public static class MyBean {
public int foo = 1;
}
// Our REST resource to test.
// Simply echos the response.
@Rest
public static class EchoRest extends BasicRestServlet {
@RestPut
public MyBean echo(@Content MyBean bean) {
return bean;
}
}
// Our JUnit test.
@Test
public void testEcho() throws Exception {
var myBean = new MyBean();
// Do a round-trip on the bean through the REST interface
myBean = MockRestClient
.create(EchoRest.class)
.json5()
.build()
.put("/echo", myBean)
.run()
.assertStatus().is(200)
.assertContent().is("{foo:1}")
.getContent().as(MyBean.class);
assertEquals(1, myBean.foo);
}
}
In This Section
📄️ 3.6.1. Choosing a REST Client
Juneau 10 ships two REST client stacks in the same release. Both are supported; they differ in
📄️ 3.6.2. POJO Marshalling
By default, JSON support is provided for HTTP request and response bodies.
📄️ 3.6.3. Request Parts
Per-client or per-request HTTP parts (headers, query/form data, path parameters) can be manipulated via the following
📄️ 3.6.4. Request Content
The request body can either be passed in with the client creator method (e.g. post(uri,body)), or can be specified via the following methods:
📄️ 3.6.5. Response Status
After execution using RestRequest.run() or RestRequest.complete(), the following methods can be used to get the response status:
📄️ 3.6.6. Response Headers
Response headers are accessed through the following methods:
📄️ 3.6.7. Response Content
The response body is accessed through the following method:
📄️ 3.6.8. Interceptors
The RestCallInterceptor API provides a quick way
🗃️ 3.6.9. REST Proxies
10 items
📄️ 3.6.10. Logging and Debugging
The following methods provide logging of requests and responses:
📄️ 3.6.11. Customizing HttpClient
The classic REST client exposes the settings of the underlying Apache HttpClient through fluent builder methods and overridable factory methods.
📄️ 3.6.12. Extending RestClient
Subclass RestClient to change how every request is executed, or plug in a RestCallHandler for the same job without subclassing.
📄️ 3.6.13. Authentication
The classic Juneau REST client delegates authentication to the underlying Apache HTTP Client, so you configure it with HTTP Client APIs or the builder shortcuts.
🗃️ 3.6.14. Next-Generation REST Client
6 items
🗃️ 3.6.15. HTTP Transport Adapters
4 items
📄️ 3.6.16. Streaming Cursors
The Marshaller class exposes streaming methods that let you consume a REST response as a
📄️ 3.6.17. Content-Type Negotiation
RestClient supports multi-format content negotiation: it automatically sets Accept and
📄️ 3.6.18. NG Client Security
The NG RestClient protects outbound calls against server-side request forgery (SSRF) and credential