This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch docs
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/docs by this push:
new 4a3f736db1 Redesign REST Proxies landing page as a capabilities
showcase
4a3f736db1 is described below
commit 4a3f736db1a41847b20f16a47a47590f238dd836
Author: James Bognar <[email protected]>
AuthorDate: Sun Jul 26 08:07:25 2026 -0400
Redesign REST Proxies landing page as a capabilities showcase
- Add end-to-end quick-start walkthrough for @RestProxy-based clients
- Add annotation-reference table linking out to each sub-page
- Showcase capabilities: path/query/formdata params, content, request,
response, and error-handling patterns
- Document previously-undocumented @Header("*") map form in a new
dedicated dynamic/multiple-headers section
- Rename title "REST Proxy" -> "REST Proxies" to match the sidebar label
Co-authored-by: Cursor <[email protected]>
---
pages/topics/13.09.00.RestProxies.md | 324 ++++++++++++++++++++---------------
1 file changed, 187 insertions(+), 137 deletions(-)
diff --git a/pages/topics/13.09.00.RestProxies.md
b/pages/topics/13.09.00.RestProxies.md
index 2d06170405..ac290e20b0 100644
--- a/pages/topics/13.09.00.RestProxies.md
+++ b/pages/topics/13.09.00.RestProxies.md
@@ -1,13 +1,20 @@
---
id: 13.09.RestProxies
-title: "REST Proxy"
+title: "REST Proxies"
slug: RestProxies
---
-One of the more powerful features of the REST client class is the ability to
produce Java interface proxies against
-arbitrary 3rd party REST resources.
+One of the most powerful features of the REST client class is the ability to
turn a plain Java interface into a
+fully-working client for a 3rd-party REST resource.
-The methods to retrieve remote interfaces are:
+You describe the API with a Java interface and a handful of annotations — the
HTTP method, the path, which
+arguments become headers, query parameters, form data, or the request body,
and what the response should be parsed
+into.
+<a href="/site/apidocs/org/apache/juneau/rest/client/classic/RestClient.html"
target="_blank">RestClient</a> then hands you
+back a dynamic proxy that implements the interface, translating every method
call into an HTTP request and every
+response back into a Java return value.
+
+The methods used to retrieve remote interfaces are:
<tree>
<node-0><java-class><a
href="/site/apidocs/org/apache/juneau/rest/client/classic/RestClient.html"
target="_blank">RestClient</a></java-class></node-0>
@@ -16,20 +23,9 @@ The methods to retrieve remote interfaces are:
<node-1><java-method><a
href="/site/apidocs/org/apache/juneau/rest/client/classic/RestClient.html#getRemote(java.lang.Class)"
target="_blank">getRemote(Class,Object,Serializer,Parser)</a></java-method></node-1>
</tree>
-Annotations are used on the interface and interface methods to specify how to
convert input and output to HTTP headers,
-query parameters, form post parameters, or request/response bodies:
+## Quick Start
-<tree>
-<node-0><java-annotation><a
href="/site/apidocs/org/apache/juneau/http/remote/Remote.html"
target="_blank">Remote</a></java-annotation> - Applied to interface
class.</node-0>
-<node-0><java-annotation><a
href="/site/apidocs/org/apache/juneau/http/remote/RemoteOp.html"
target="_blank">RemoteOp</a></java-annotation> - Applied to interface
methods.</node-0>
-<node-0><java-annotation><a
href="/site/apidocs/org/apache/juneau/http/Content.html"
target="_blank">Content</a></java-annotation></node-0>
-<node-0><java-annotation><a
href="/site/apidocs/org/apache/juneau/http/Header.html"
target="_blank">Header</a></java-annotation></node-0>
-<node-0><java-annotation><a
href="/site/apidocs/org/apache/juneau/http/FormData.html"
target="_blank">FormData</a></java-annotation></node-0>
-<node-0><java-annotation><a
href="/site/apidocs/org/apache/juneau/http/Query.html"
target="_blank">Query</a></java-annotation></node-0>
-<node-0><java-annotation><a
href="/site/apidocs/org/apache/juneau/http/Path.html"
target="_blank">Path</a></java-annotation></node-0>
-<node-0><java-annotation><a
href="/site/apidocs/org/apache/juneau/http/Request.html"
target="_blank">Request</a></java-annotation></node-0>
-<node-0><java-annotation><a
href="/site/apidocs/org/apache/juneau/http/Response.html"
target="_blank">Response</a></java-annotation></node-0>
-</tree>
+Define an interface, annotate it, and get a proxy from a `RestClient`:
:::tip Example
```java
@@ -42,6 +38,9 @@ public interface PetStore {
@Header("E-Tag") UUID etag,
@Query("debug") boolean debug
);
+
+ @RemoteGet("/pets/{petId}")
+ Pet getPet(@Path("petId") int petId);
}
```
@@ -52,13 +51,16 @@ RestClient client = RestClient.create().json5().build();
// Instantiate our proxy interface.
PetStore store = client.getRemote(PetStore.class, "http://localhost:10000");
-// Use it to create a pet.
+// Use it to create a pet...
CreatePet createPet = new CreatePet("Fluffy", 9.99);
Pet pet = store.addPet(createPet, UUID.randomUUID(), true);
+
+// ...and fetch it back.
+Pet sameCat = store.getPet(pet.getId());
```
:::
-The call above translates to the following REST call:
+The `addPet(...)` call above translates to a real HTTP request under the hood:
```text
POST http://localhost:10000/petstore/pets?debug=true HTTP/1.1
@@ -72,180 +74,228 @@ E-Tag: 475588d4-0b27-4f56-9296-cc683251d314
}
```
-The <a href="/site/apidocs/org/apache/juneau/http/remote/RemoteOp.html"
target="_blank">@RemoteOp</a> annotations can be eliminated if you use specific
naming conventions on your method names to identify
-the HTTP method and path.
+No hand-written URL building, serialization, or response parsing required —
the interface *is* the contract.
-:::tip Example
-```java
-@Remote(path="/petstore")
-public interface PetStore {
+:::info See Also
+The <a href="/site/apidocs/org/apache/juneau/http/remote/RemoteOp.html"
target="_blank">@RemoteOp</a> annotation (and
+its `method`/`path`) can often be skipped entirely if your Java method names
follow a simple naming convention. See
+[@RemoteOp](/docs/topics/RemoteMethod) for the inference rules.
+:::
- // @RemoteOp optional since method and path is inferred from method name.
- String postPets(@Content CreatePet pet);
-}
+## Annotation Reference
+
+Method behavior is driven entirely by annotations on the interface, its
methods, and their parameters:
+
+| Annotation | Applies to | Purpose |
+|---|---|---|
+| [`@Remote`](/docs/topics/Remote) | Interface | Marks the interface as a REST
proxy; sets the base path, common headers, and client versioning. |
+| [`@RemoteOp`](/docs/topics/RemoteMethod) (and
`@RemoteGet`/`@RemotePost`/`@RemotePut`/`@RemotePatch`/`@RemoteDelete`) |
Method | Maps a method to an HTTP verb + path. Often optional if the method
name implies the verb. |
+| [`@Path`](/docs/topics/Path) | Parameter | URL path variables
(`{petId}`-style placeholders). |
+| [`@Query`](/docs/topics/Query) | Parameter | Query-string parameters. |
+| [`@Header`](/docs/topics/Header) | Parameter | HTTP request headers —
including dynamic, multi-value forms. |
+| [`@FormData`](/docs/topics/FormData) | Parameter |
`application/x-www-form-urlencoded` body parameters. |
+| [`@Content`](/docs/topics/Content) | Parameter | The HTTP request body. |
+| [`@Request`](/docs/topics/Request) | Parameter or class | Bundles multiple
parts (content/headers/query/etc.) behind a single request bean. |
+| [`@Response`](/docs/topics/Response) | Return type | Bundles
content/headers/status behind a single response bean. |
+
+Every one of these supports both **single-value** forms (`@Query("limit") int
limit`) and **dynamic, multi-value**
+forms (`@Query Map<String,Object> params`) — see the [dynamic
headers](#dynamic-and-multiple-headers) example below
+for the pattern in detail.
+
+## Capabilities Showcase
+
+The sections below are short teasers of what's possible. Each links to a
deep-dive page with the full set of
+supported types and edge cases.
+
+### Sending data to the server
+
+**Path variables** ([@Path](/docs/topics/Path)) fill in `{placeholders}` in
the URL:
+
+```java
+@RemoteGet("/pets/{petId}")
+Pet getPet(@Path("petId") int petId);
```
-:::
-## Default Values
+**Query parameters** ([@Query](/docs/topics/Query)) — and the equivalent for
+[@FormData](/docs/topics/FormData) form posts:
-As of Juneau 9.2.0, you can specify default values for method parameters using
the `def` attribute. Defaults can be specified either at the **method level**
(on the method itself) or at the **parameter level** (on individual
parameters). Parameter-level defaults take precedence when both are present.
+```java
+@RemoteGet("/pets")
+Pet[] findPets(@Query("species") String species, @Query("limit") int limit);
+```
-### Basic Usage - Parameter-Level Defaults
+#### Dynamic and multiple headers
-The most straightforward approach is to specify defaults directly on the
parameters:
+[@Header](/docs/topics/Header) (and `@Query`/`@FormData`/`@Path`) isn't
limited to one header per parameter. Leave
+the name off (or use the explicit wildcard `"*"`) and pass a `Map`, a
`HeaderList`, or a bean — every entry or
+property is expanded into its own header:
-:::tip Example
```java
@Remote(path="/petstore")
public interface PetStore {
+ // Every map entry becomes its own header.
@RemoteGet("/pets")
- Pet[] getPets(
- @Header(name="Accept-Language", def="en-US") String language,
- @Query(name="limit", def="10") Integer limit
- );
+ Pet[] getPets(@Header("*") Map<String,Object> headers);
+
+ // Same idea via a pre-built HeaderList...
+ @RemoteGet("/pets")
+ Pet[] getPetsViaHeaderList(@Header HeaderList headers);
+
+ // ...or a bean whose properties become individual headers.
+ @RemoteGet("/pets")
+ Pet[] getPetsViaBean(@Header PetHeaders headers);
}
```
```java
-PetStore store = client.getRemote(PetStore.class, "http://localhost:10000");
+Map<String,Object> headers = JsonMap.of("X-Client-Id", "acme", "X-Trace-Id",
UUID.randomUUID());
+Pet[] pets = store.getPets(headers);
+```
-// Uses default language="en-US" and limit=10
-Pet[] pets1 = store.getPets(null, null);
+This is a great way to forward a variable, caller-supplied set of headers
(correlation IDs, feature flags, tenant
+context, etc.) without adding a new interface parameter every time one shows
up. See
+[@Header](/docs/topics/Header) for the full list of supported dynamic forms.
-// Uses custom language, default limit=10
-Pet[] pets2 = store.getPets("fr-FR", null);
+**Request bodies** ([@Content](/docs/topics/Content)) accept any serializable
POJO, or raw `Reader`/`InputStream`:
-// Uses default language="en-US", custom limit
-Pet[] pets3 = store.getPets(null, 25);
+```java
+@RemotePost("/pets")
+Pet addPet(@Content CreatePet pet);
```
-:::
-The above examples translate to the following REST calls:
+**Request beans** ([@Request](/docs/topics/Request)) bundle several of the
above behind one argument, so callers pass
+a single object instead of a long parameter list:
-```text
-GET http://localhost:10000/petstore/pets?limit=10 HTTP/1.1
-Accept-Language: en-US
+```java
+@RemotePost
+String postPet(CreatePetRequest bean);
+```
+```java
+@Request
+public class CreatePetRequest {
+
+ @Content
+ public CreatePet getContent() { ... }
-GET http://localhost:10000/petstore/pets?limit=10 HTTP/1.1
-Accept-Language: fr-FR
+ @Query
+ public Map<String,Object> getQueryParams() { ... }
-GET http://localhost:10000/petstore/pets?limit=25 HTTP/1.1
-Accept-Language: en-US
+ @Header("E-Tag")
+ public UUID getUUID() { ... }
+}
```
-### Supported Annotations
+### Getting results back
-Default values are supported on the following annotations:
+Return types are flexible — parsed POJOs, raw streams, async wrappers, or just
the status code:
-- `@Header(name="...", def="...")` - HTTP request headers
-- `@Query(name="...", def="...")` - Query string parameters
-- `@FormData(name="...", def="...")` - Form post parameters
-- `@Path(name="...", def="...")` - Path variables
-- `@Content(def="...")` - Request body (new in 9.2.0)
+```java
+@Remote(path="/petstore")
+public interface PetStore {
-### Method-Level Defaults (Alternative Approach)
+ @RemoteGet("/pets") Pet[] getPets();
// Parsed POJO
+ @RemoteGet(path="/pets", returns=STATUS) int getPetsStatus();
// Just the HTTP status
+ @RemoteGet("/pets") Future<Pet[]>
getPetsAsync(); // Async
+ @RemoteGet("/pets/{petId}") InputStream
getPetRaw(@Path("petId") int petId); // Raw stream
+}
+```
-You can also specify defaults at the method level, which can be useful for
interface-level configuration:
+See [@RemoteOp](/docs/topics/RemoteMethod) for the complete list of supported
return types.
+
+**Response beans** ([@Response](/docs/topics/Response)) do the same thing in
reverse — bundle the body, headers, and
+status code of a response behind one typed object:
-:::tip Example
```java
-@Remote(path="/api")
-public interface MyApi {
-
- @RemotePost("/resource")
- @Header(name="X-API-Key", def="default-key")
- @Header(name="X-Client-Version", def="1.0")
- @Query(name="format", def="json")
- @Content(def="{}")
- String createResource(
- @Header("X-API-Key") String apiKey,
- @Header("X-Client-Version") String clientVersion,
- @Query("format") String format,
- @Content String data
- );
+@RemotePost
+CreatePetResponse postPet(@Content CreatePet pet);
+```
+```java
+@Response
+public interface CreatePetResponse {
+
+ @Content Pet getContent();
+ @Header("E-Tag") UUID getUUID();
+ @StatusCode int getStatus();
}
```
+**Error handling** happens automatically through checked exceptions. Declare a
`throws` clause using any of the
+roughly 60 predefined <a
href="/site/apidocs/org/apache/juneau/http/response/package-summary.html"
target="_blank">HTTP response exceptions</a>
+(or your own), and Juneau throws the matching one when the server returns that
status code:
+
```java
-// All parameters null - all defaults applied
-String result = api.createResource(null, null, null, null);
-// POST /api/resource?format=json
-// X-API-Key: default-key
-// X-Client-Version: 1.0
-// Content: {}
+@RemoteGet("/pets/{petId}")
+Pet getPet(@Path("petId") int petId) throws NotFound;
+```
-// Mix of provided and null values
-String result = api.createResource("my-key", null, "xml", "{data:true}");
-// POST /api/resource?format=xml
-// X-API-Key: my-key
-// X-Client-Version: 1.0
-// Content: {data:true}
+```java
+try {
+ Pet pet = store.getPet(999);
+} catch (NotFound e) {
+ // HTTP 404 was returned - Juneau matched the status to the declared
exception type.
+}
```
-:::
-### Precedence: Parameter vs. Method Level
+## Power-User Patterns
-When defaults are specified at both the parameter and method level, the
**parameter-level default takes precedence**:
+### Default Values
-:::tip Example
-```java
-@Remote(path="/api")
-public interface MyApi {
+Default values let you specify fallbacks for `null` arguments right on the
annotation, so callers don't have to
+pass every parameter every time:
- @RemoteGet("/data")
- @Query(name="format", def="xml") // Method-level default
- String getData(
- @Query(name="format", def="json") String format // Parameter-level
default (takes precedence)
- );
-}
+```java
+@RemoteGet("/pets")
+Pet[] getPets(
+ @Header(name="Accept-Language", def="en-US") String language,
+ @Query(name="limit", def="10") Integer limit
+);
```
```java
-// Uses parameter-level default: format=json
-String result = api.getData(null);
+// Uses default language="en-US" and limit=10.
+Pet[] pets = store.getPets(null, null);
```
-:::
-This precedence allows you to:
-- Define common defaults at the method level for multiple parameters
-- Override specific parameters with more specific defaults at the parameter
level
-- Keep your interface clean by placing defaults where they're most relevant
+`def` is supported on [@Header](/docs/topics/Header),
[@Query](/docs/topics/Query), [@FormData](/docs/topics/FormData),
+[@Path](/docs/topics/Path), and [@Content](/docs/topics/Content), at either
the parameter or method level (parameter
+wins when both are set).
-### Content Body Defaults
+### Dual-Purpose Interfaces
-The `@Content` annotation now supports a `def` attribute for specifying a
default request body:
+The *same* Java interface can define both the client proxy and the server
implementation, so a signature change is
+caught by the compiler on both sides instead of drifting apart at runtime:
-:::tip Example
```java
@Remote(path="/petstore")
public interface PetStore {
-
- @RemotePost("/pets")
- @Content(def="{name:'Unknown',price:0}")
- Pet addPet(@Content CreatePet pet);
+ @RemoteGet("/pet")
+ Collection<Pet> getPets() throws NotAcceptable;
}
```
-
```java
-// When pet is null, sends default JSON
-Pet result = store.addPet(null);
-// POST /petstore/pets
-// Content-Type: application/json
-// Content: {name:'Unknown',price:0}
+@Rest(path="/petstore")
+public class PetStoreResource extends BasicRestServlet implements PetStore {
+ @Override
+ @RestOp(method=GET, path="/pet")
+ public Collection<Pet> getPets() throws NotAcceptable {
+ return store.getPets();
+ }
+}
```
-:::
-### Use Cases
-
-Default values are particularly useful for:
-
-1. **API Keys and Authentication**: Provide default credentials that can be
overridden per call
-2. **Versioning**: Specify default API versions
-3. **Pagination**: Set default page sizes and limits
-4. **Content Negotiation**: Specify default content types and languages
-5. **Feature Flags**: Enable/disable features with default values
-
-:::note
-Default values are only applied when the parameter value is `null`. Empty
strings, zero values, and empty collections are considered valid values and
will not trigger the default.
-:::
\ No newline at end of file
+See [Dual-purpose (end-to-end) interfaces](/docs/topics/DualPurposeInterfaces)
for the full walkthrough, including how
+parameter-level annotations like `@Header` and `@Path` are inherited from the
interface for free.
+
+:::info See Also
+- [@Remote](/docs/topics/Remote) - Interface-level configuration: base path,
common headers, versioning.
+- [@RemoteOp](/docs/topics/RemoteMethod) - HTTP method/path mapping,
naming-convention inference, return types.
+- [@Content](/docs/topics/Content) - The request body.
+- [@FormData](/docs/topics/FormData) - Form post parameters.
+- [@Query](/docs/topics/Query) - Query-string parameters.
+- [@Header](/docs/topics/Header) - Request headers, including
dynamic/multi-value forms.
+- [@Path](/docs/topics/Path) - URL path variables.
+- [@Request](/docs/topics/Request) - Bean-style bundling of request parts.
+- [@Response](/docs/topics/Response) - Bean-style bundling of response parts.
+- [Dual-purpose (end-to-end) interfaces](/docs/topics/DualPurposeInterfaces) -
Sharing one interface between client and server.
+:::