Skip to content

Instantly share code, notes, and snippets.

@danbev
Created November 27, 2012 06:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danbev/4152794 to your computer and use it in GitHub Desktop.
Save danbev/4152794 to your computer and use it in GitHub Desktop.
RESTFul Endpoints in AeroGear Controller

RESTFul Endpoints in AeroGear Controller

The plan for AeroGear Controller is to utilize RestEasy which is a JAX-RS implementation. There is really nothing in the JAX-RS specification that we require, but more some of the functionality that the exists in RestEasy. This document will try to list what this functionality is and if we should really use a JAX-RS implementation of write/"borrow" what we need.

AeroGear controller current has routing support for POJO endpoints, but current routing will forward the result of such an endpoint to a view. For a RESTful POJO endpoint we would return the data in the content type that the caller asks for (using the Http Accept header).

Working Branch

Features Required

The following features have been identified as required by AeroGear Controller.

Content Negotiation

Clients use the Http Accept header to specify the format they wish the data to be returned in. By inspecting this header we can determine the type of response that is to be provided, either a forward to a view or return data in the requested format.

When declaring routes we could specify a produces method to specify the content type of the response:

route()
       .from("/path/{id}")
       .on(RequestMethod.GET)
       .produces("application/json")
       .to(HelloRest.class).path(pathParam(String.class, "id"));

Design

For incoming requests there is no difference between a MVC route and a RESTful route. The difference is in how the response is handled. For a MVC route, the result from the route target method should be forwarded to a view, and for RESTFul requests the result should be returned to the caller in the format specified in the Accept header, provided that the route supports this type.

For this we have introduced a Responder interface:

public interface Responder {
    
    boolean accepts(final String mediaType);
    
    void respond(final Object entity, final RouteContext routeContext) throws Exception;

}

We would implement a concrete implementation for a MVC route, for example a MvcResponder which would forward to a view if the Access header is 'text/html' for example.
For RESTFul request we might introduce an AbstractRestResponder, for which a concrete implementation might look something like this:

public class JsonResponder extends AbstractRestResponder {

    public JsonResponder() {
        super(MediaType.JSON.toString());
    }

    public void writeResponse(final Object entity, final RouteContext routeContext) throws Exception {
        final ObjectMapper om = new ObjectMapper();
        om.writeValue(routeContext.getResponse().getWriter(), entity);
    }

}

AbstractRestResponder will take care for common things like, implementing the accepts method, and also for making sure that the response's Content-Type response header is always set. To add a custom type, a user would be required to implement something like this:

public class CustomMediaTypeResponder extends AbstractRestResponder {

    public CustomMediaTypeResponder() {
        super("application/custom");
    }

    public void writeResponse(Object entity, RouteContext routeContext) throws Exception {
        routeContext.getResponse().getWriter().write("custom object being returned" + entity);
    }

}

Provided that the above class is package in CDI scannable jar, one with a beans.xml, this will be enabled and available to AeroGear Controller.

Example

There is a version of aerogear-controller-demo which as been updated with the work so far.
Working Branch

The demo has had one route added to it:

route()
   .from("/rest")
   .on(RequestMethod.GET)
   .produces(MediaType.JSON.toString(), MediaType.HTML.toString(), "application/custom")
   .to(RestEndpoint.class).get();

The RestEndpoint's get method simple returns a Car instance:

public class RestEndpoint {   
    public Car get() {
        return new Car("REST Color", "REST Brand");
    }
}

JSON Request:

curl -i --header "Accept: application/json" "http://controllerdemo-danbev.rhcloud.com/aerogear-controller-demo/rest" 
HTTP/1.1 200 OK
Date: Mon, 03 Dec 2012 12:46:48 GMT
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 43
Vary: Accept-Encoding
Strict-Transport-Security: max-age=15768000, includeSubDomains

{"color":"REST Color","brand":"REST Brand"}

HTML Request

curl -i --header "Accept: text/html" "http://controllerdemo-danbev.rhcloud.com/aerogear-controller-demo/rest" 
HTTP/1.1 200 OK
Date: Mon, 03 Dec 2012 12:48:39 GMT
Server: Apache-Coyote/1.1
X-Powered-By: JSP/2.2
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 1901
Set-Cookie: JSESSIONID=O10UbWTrmCbjIiMeDJZUFhzu; Path=/aerogear-controller-demo
Vary: Accept-Encoding
Strict-Transport-Security: max-age=15768000, includeSubDomains

html document here (not displayed to make this readable)

Custom Content-Type Request Example:

 curl -i --header "Accept: application/custom" "http://controllerdemo-danbev.rhcloud.com/aerogear-controller-demo/rest"
HTTP/1.1 200 OK
Date: Mon, 03 Dec 2012 12:50:36 GMT
Server: Apache-Coyote/1.1
Content-Type: application/custom;charset=UTF-8
Content-Length: 71
Vary: Accept-Encoding
Strict-Transport-Security: max-age=15768000, includeSubDomains

custom object being returnedCar{color='REST Color', brand='REST Brand'}

This might look strange that this last example returned JSON too, but this is just toString() representation for the Car class.

Pros

  • No additional dependency to a JAX-RS implementation (smaller deployment)
  • We only implement what we need.

Cons

  • Using a JAX-RS implementation would provide an implementation that was been working in production environments for many years.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment