MethodExampleResource

The MethodExampleResource class provides examples of the following:

The resource is provided to show how various HTTP entities (e.g. parameters, headers) can be accessed as either annotated Java parameters, or through methods on the RestRequest object.

MethodExampleResource.java

/** * Sample REST resource that shows how to define REST methods and OPTIONS pages */ @RestResource( path="/methodExample", messages="nls/MethodExampleResource", htmldoc=@HtmlDoc( navlinks={ "up: servlet:/..", "options: servlet:/?method=OPTIONS", "source: $C{Source/gitHub}/org/apache/juneau/examples/rest/$R{servletClassSimple}.java" }, aside={ "<div style='max-width:400px' class='text'>", " <p>Shows the different methods for retrieving HTTP query/form-data parameters, headers, and path variables.</p>", " <p>Each method is functionally equivalent but demonstrate different ways to accomplish the same tasks.</p>", "</div>" } ) ) public class MethodExampleResource extends BasicRestServlet { private static final UUID SAMPLE_UUID = UUID.fromString("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"); private static final String SAMPLE_UUID_STRING = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"; /** Example GET request that redirects to our example method */ @RestMethod(name=GET, path="/") public ResourceDescription[] doExample() throws Exception { return new ResourceDescription[] { new ResourceDescription( "example1/foo/123/"+SAMPLE_UUID+"/path-remainder?q1=456&q2=bar", "Example 1 - Annotated method attributes." ), new ResourceDescription( "example2/foo/123/"+SAMPLE_UUID+"/path-remainder?q1=456&q2=bar", "Example 2 - Low-level RestRequest/RestResponse objects." ), new ResourceDescription( "example3/foo/123/"+SAMPLE_UUID+"/path-remainder?q1=456&q2=bar", "Example 3 - Intermediate-level APIs." ) }; } /** * Methodology #1 - GET request using annotated attributes. * This approach uses annotated parameters for retrieving input. */ @RestMethod(name=GET, path="/example1/{p1}/{p2}/{p3}/*") public String example1( @Method String method, // HTTP method. @Path("p1") String p1, // Path variables. @Path("p2") int p2, @Path("p3") UUID p3, @Query("q1") int q1, // Query parameters. @Query("q2") String q2, @Query("q3") UUID q3, @Path("/*") String remainder, // Path remainder after pattern match. @Header("Accept-Language") String lang, // Headers. @Header("Accept") String accept, @Header("DNT") int doNotTrack ) { // Send back a simple Map response return new AMap<String,Object>() .append("method", method) .append("path-p1", p1) .append("path-p2", p2) .append("path-p3", p3) .append("remainder", remainder) .append("query-q1", q1) .append("query-q2", q2) .append("query-q3", q3) .append("header-lang", lang) .append("header-accept", accept) .append("header-doNotTrack", doNotTrack); } /** * Methodology #2 - GET request using methods on RestRequest and RestResponse. * This approach uses low-level request/response objects to perform the same as above. */ @RestMethod(name=GET, path="/example2/{p1}/{p2}/{p3}/*") public void example2( RestRequest req, // A direct subclass of HttpServletRequest. RestResponse res // A direct subclass of HttpServletResponse. ) { // HTTP method. String method = req.getMethod(); // Path variables. RequestPathMatch path = req.getPathMatch(); String p1 = path.get("p1", String.class); int p2 = path.get("p2", int.class); UUID p3 = path.get("p3", UUID.class); // Query parameters. RequestQuery query = req.getQuery(); int q1 = query.get("q1", 0, int.class); String q2 = query.get("q2", String.class); UUID q3 = query.get("q3", UUID.class); // Path remainder after pattern match. String remainder = req.getPathMatch().getRemainder(); // Headers. String lang = req.getHeader("Accept-Language"); String accept = req.getHeader("Accept"); int doNotTrack = req.getHeaders().get("DNT", int.class); // Send back a simple Map response Map<String,Object> m = new AMap<String,Object>() .append("method", method) .append("path-p1", p1) .append("path-p2", p2) .append("path-p3", p3) .append("remainder", remainder) .append("query-q1", q1) .append("query-q2", q2) .append("query-q3", q3) .append("header-lang", lang) .append("header-accept", accept) .append("header-doNotTrack", doNotTrack); res.setOutput(m); // Use setOutput(Object) just to be different. } /** * Methodology #3 - GET request using special objects. * This approach uses intermediate-level APIs. * The framework recognizes the parameter types and knows how to resolve them. */ @RestMethod(name=GET, path="/example3/{p1}/{p2}/{p3}/*") public String example3( HttpMethod method, // HTTP method. RequestPathMatch path, // Path variables. RequestQuery query, // Query parameters. RequestHeaders headers, // Headers. AcceptLanguage lang, // Specific header classes. Accept accept ) { // Path variables. String p1 = path.get("p1", String.class); int p2 = path.get("p2", int.class); UUID p3 = path.get("p3", UUID.class); // Query parameters. int q1 = query.get("q1", 0, int.class); String q2 = query.get("q2", String.class); UUID q3 = query.get("q3", UUID.class); // Path remainder after pattern match. String remainder = path.getRemainder(); // Headers. int doNotTrack = headers.get("DNT", int.class); // Send back a simple Map response return new AMap<String,Object>() .append("method", method) .append("path-p1", p1) .append("path-p2", p2) .append("path-p3", p3) .append("remainder", remainder) .append("query-q1", q1) .append("query-q2", q2) .append("query-q3", q3) .append("header-lang", lang) .append("header-accept", accept) .append("header-doNotTrack", doNotTrack); } }

The class consists of 4 methods:

There's a lot going on in this method.
Notice how you're able to access URL attributes, parameters, headers, and content as parsed POJOs.
All the input parsing is already done by the toolkit.
You simply work with the resulting POJOs.

As you might notice, using annotations typically results in fewer lines of code and are therefore usually preferred over the API approach, but both are equally valid.

When you visit this page through the router page, you can see the top level page:

http://localhost:10000/methodExample

Clicking the first link on the page results in this page:

http://localhost:10000/methodExample/example1/foo/123/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/path-remainder?q1=456&q2=bar

Notice how the conversion to POJOs is automatically done for us, even for non-standard POJOs such as UUID.

Self-documenting design through Swagger OPTIONS pages

One of the main features of Juneau is that it produces OPTIONS pages for self-documenting design (i.e. REST interfaces that document themselves).

Much of the information populated on the OPTIONS page is determined through reflection.
This basic information can be augmented with information defined through:

OPTIONS pages are simply serialized {@link oaj.dto.swagger.Swagger} DTO beans.
Localized versions of these beans are retrieved using the {@link oajr.RestRequest#getSwagger()} method.

To define an OPTIONS request handler, the {@link oajr.BasicRestServlet} class defines the following Java method:

BasicRestServlet.java

/** OPTIONS request handler */ @RestMethod(name=OPTIONS, path="/*") public Swagger getOptions(RestRequest req) { return req.getSwagger(); }

The OPTIONS link that you see on the HTML version of the page is created through a property defined by the {@link oaj.html.HtmlDocSerializer} class and specified on the resource class annotation:

@RestResource( htmldoc=@HtmlDoc( navlinks={ "options: servlet:/?method=OPTIONS" } ) )

This simply creates a link that's the same URL as the resource URL appended with "?method=OPTIONS", which is a shorthand way that the framework provides of defining overloaded GET requests.
Links using relative or absolute URLs can be defined this way.

Clicking the options link on the page presents you with information about how to use this resource:

http://localhost:10000/methodExample/?method=OPTIONS

This page (like any other) can also be rendered in JSON or XML by using the &Accept URL parameter.