RSS

Tag Archives: REST

What is HATEOAS?

With probably the most unpronounceable acronym in the world of IT, and there are a lot, HATEOAS is also one of the most obscure and misunderstood constraints of the REST specification. In this article I will make an attempt to shed some light on the world of hypermedia and HATEOAS.

Let’s start with listing the complete list of the constraints of REST to give us some context on where the HATEOAS constraint sits in the REST constraints.

  1. Client-server
  2. Stateless server
  3. Cache
  4. Uniform interface
    1. Identification of resources
    2. Manipulation of resources through representations
    3. Self-descriptive messages
    4. Hypermedia as the engine of application state (HATEOAS)
  5. Layered System
  6. Code-on-Demand

 

As we just saw in the list of constraints HATEOAS stands for “Hypermedia As The Engine Of Application State”. So, what does this mean?

To grab the concept it’s helpful to think of a regular webpage. When browsing to a page there are, most of the time, various hyperlinks available which you can use to navigate further to other pages. These pages are essentially the “state” of the web(application).

To quote Roy Fielding from his thesis about RESTful architecture and design:

“The name ‘Representational State Transfer’ is intended to evoke an image of how a well-designed Web application behaves: a network of web pages (a virtual state-machine), where the user progresses through the application by selecting links (state transitions), resulting in the next page (representing the next state of the application) being transferred to the user and rendered for their use.”-Roy Fielding
Architectural Styles and the Design of Network-based Software Architectures
Chapter 6

So in essence the states are the various webpages and the transitions to the different states are the hyperlinks. The hyperlinks are the “engine” to which the state transfer can occur.

Hateoas_web

Above we see a diagram of various webpages, when starting to browse the internet we start at an initial starting page. Clicking links on this starting page we can browse to other pages. Except from changing the URL manually in the browser address bar, the pages we can open via hyperlinks are dictated by the first page itself. The state transitions are controlled by the web application. This is an important concept in HATEOAS as well. Also the endpoints are hidden from an end user perspective.

As stated above the webpages are states of the (web)application and the hyperlinks are the mechanism (engine) to changing the state. We can abstract our webpages diagram also to this:

Hateoas_api

So… how does this applies to REST and HATEOAS?

The way to implement HATEOAS is pretty straightforward: in each response message add the link(s) for possible next request messages. Therefore give the opportunity to the consumer of the REST service to transition the state via the links in the response message.

A very simplified example of a HATEOAS response:

{
  "stocklist": {
    "name": "ACME",
    "price": "10.00",
    "link": [
      {
        "rel": "self",
        "href": "/stock/ACME",
        "method": "get"
      },
      {
        "rel": "buy",
        "href": "/account/ACME/buy",
        "method": "post"
      },
      {
        "rel": "sell",
        "href": "/account/ACME/sell",
        "method": "post"
      }
    ]
  }
}

In our simple example response we request the stock information for ACME. We get the price back, but in addition the links to either buy or sell the stock.

So when we add links to our responses does that mean we are now truly RESTfull?

Again, Roy Fielding seems pretty clear about this:

“If the engine of application state (and hence the API) is not being driven by hypertext, then it cannot be RESTful and cannot be a REST API. Period. Is there some broker manual somewhere that needs to be fixed?”-Roy Fielding
REST APIs must be hypertext-driven
Untangled: Musings of Roy T. Fielding

But, are we truly RESTful if we include hyperlinks in our responses?

The thing is, people do not use REST API’s. People use apps and sites and those apps and site use those REST API’s. So what does this mean? It means that if the app or site developer chooses not to use the HATEOAS links in the response the end user cannot state transition using hypermedia and ergo we are not truly HATEOAS compliant and thus RESTful.

So, adding the links in the responses is an important step in HATEOAS and RESTful compliant, it is only the first step.

HATEOAS is one of the more misunderstood and forgotten REST constraints. I hope this blogpost will help you in better grasp this REST constraint.

Advertisements
 
Leave a comment

Posted by on 2016-03-18 in API Management

 

Tags: , , , , , ,

Exploring the SimpleConsumer and Default Camel CXFRS binding styles

When exposing your Camel route as a Rest service using CXFRS there has to be a translation from the CXFRS message to the Camel exchange. This translation is covered in the bindingStyle used in Camel. The Camel CXFRS component has three different binding styles:

  • SimpleConsumer
  • Default
  • Custom

In this blogpost we will explore some of the characteristics and differences between the SimpleConsumer and the Default binding styles. This will be a brief overview, so not all aspects of the binding styles will be covered in this post.

Project setup

In order to show some of the characteristics and differences between the two binding styles we will create a dummy Fuse project. The project contains 1 Camel route and exposes this route as a REST service using CXFRS.

The CXFRS class looks like this:


package nl.rubix.cxfrs.service;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/")
public class Endpoint {
    
    @GET
    @Path("/test/{id}/{id2}")
    @Produces(MediaType.APPLICATION_JSON)
    public String getAssets(@PathParam("id") String id, @PathParam("id2") String id2){
        return null;
    }

    
}

The Camel route looks like this:


<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:camel="http://camel.apache.org/schema/blueprint"
       xmlns:cxf="http://camel.apache.org/schema/blueprint/cxf"
       xsi:schemaLocation="
       http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
       http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">

  <cxf:rsServer id="rsServer" address="http://localhost:2345/testendpoint" serviceClass="nl.rubix.cxfrs.service.Endpoint" loggingFeatureEnabled="false" />

  <camelContext trace="false" id="blueprintContext" xmlns="http://camel.apache.org/schema/blueprint">
    <route customId="true" id="cxfrs.service">
        <from uri="cxfrs:bean:rsServer?bindingStyle=Default"/>
        <log message="The message contains ${body[0]}"/>
        <marshal>
            <json library="Jackson"/>
        </marshal>
    </route>
</camelContext>

</blueprint>

The first thing to note is the fact we do not have any message transformations within the route. This means the request passed to the server is simply marshalled as a JSON document and returned as a response message.

With our GET operation the bindingStyle does not affect the response message. So when using SimpleConsumer or Default binding style, the response message is the same.

simple_vs_default_binding_1

This is because of two reasons: first both the default and SimpleConsumer binding styles the Camel message is populated with the raw CXFRS message, an object of type MessageContentsList. This is the behaviour of the Default binding style. However, the SimpleConsumer looks at the method signature of the method in the Endpoint class. When a single object can be identified as the body the Camel Message will be populated with this particular object. When a sigle object cannot be identified as the body the original MessageContentsList is used as the body in the Camel message. Since our GET operation uses two pathParam arguments the SimpleConsumer binding cannot decide which one of them is the body and defaults to the MessageContentsList object.

This behaviour is explained in more detail in the Camel documentation: http://camel.apache.org/cxfrs.html

Another thing to note is the fact the standard Camel Type converters can handle the MessageContentsList object type and marshal it to a JSON object without any trouble or configuration.

It is even possible to use the Simple index expression on the body of the Camel message. So for example this statements:


<log message=<strong>"The message contains ${body[0]}"</strong>/>

Outputs the following: The message contains 1

When the request URL was: http://localhost:2345/testendpoint/test/1/bla

SimpleConsumer binding

To use the SimpleConsumer binding in stead of the Default binding we simply add the following property to our component URI: “bindingStyle=SimpleConsumer”

The SimpleConsumer bindingStyle provides standard behaviour regarding the mapping of the CXFRS message to a Camel Exchange and Message. It does this according the following specs (again from the Camel documentation:

“In contrast, the SimpleConsumer binding style performs the following mappings, in order to make the request data more accessible to you within the Camel Message:

  • JAX-RS Parameters (@HeaderParam, @QueryParam, etc.) are injected as IN message headers. The header name matches the value of the annotation.
  • The request entity (POJO or other type) becomes the IN message body. If a single entity cannot be identified in the JAX-RS method signature, it falls back to the original MessageContentsList.
  • Binary @Multipart body parts become IN message attachments, supporting DataHandler, InputStream, DataSource and CXF’s Attachment class.
  • Non-binary @Multipart body parts are mapped as IN message headers. The header name matches the Body Part name.

Additionally, the following rules apply to the Response mapping:

  • If the message body type is different to javax.ws.rs.core.Response (user-built response), a new Response is created and the message body is set as the entity (so long it’s not null). The response status code is taken from the Exchange.HTTP_RESPONSE_CODE header, or defaults to 200 OK if not present.
  • If the message body type is equal to javax.ws.rs.core.Response, it means that the user has built a custom response, and therefore it is respected and it becomes the final response.

In all cases, Camel headers permitted by custom or default HeaderFilterStrategy are added to the HTTP response.”

This means the path parameters in our request are accessible as Camel headers on the exchange.

So we can access the id parameter using this expression:


<log message=<strong>"The message contains ${header.id}"</strong>/>

In our simple example using a GET with just two path parameters it might seem a bit trivial to use the SimpleConsumer binding over the Default. However when method signatures become more complex the SimpleConsumer binding provides very usefull functionality and can save time mapping the CXFRS MessageContentsList message manually.

Default Binding

The Default binding is, as the name suggests, the default J so no additional configuration on the component is required. However to be more verbose, one can add the bindingStyle=Default to the URI.

The Default binding style always populates the Camel message body with the CXFRS MessageContentsList object. As we have seen above we can use the Camel Simple list expressions to retrieve values from this MessageContentsList. And this can be a quick way to access values where the options are limited. However, when using more complex method signatures in your CXFRS methods this can become quite cumbersome. Especially when the values in the method signature are more exotic Java Objects.

The most common way for using the Default binding is to implement a custom Camel processor to perform the mapping manually.

For this post a sample processor is created which concatenates both values from the path parameters. The processor looks like this:


package nl.rubix.cxfrs.service;

import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.cxf.message.MessageContentsList;

public class MyCustomProcessor implements Processor{

    @Override
    public void process(Exchange exchange) throws Exception {
        // retrieve the MessageContentsList object from the Camel exchange
        MessageContentsList cxfMessage = exchange.getIn().getBody(MessageContentsList.class);
        
        // The new body message will be a simple String which holds the concatenated values of the MessageContentsList
        String camelBody = cxfMessage.get(0).toString() + "-" + cxfMessage.get(1).toString();
        
        exchange.getIn().setBody(camelBody);
        
    }

}

To use this processor in our Camel Route the route is modified and now looks like this:


<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:camel="http://camel.apache.org/schema/blueprint"
       xmlns:cxf="http://camel.apache.org/schema/blueprint/cxf"
       xsi:schemaLocation="
       http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
       http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">

  <cxf:rsServer id="rsServer" address="http://localhost:2345/testendpoint" serviceClass="nl.rubix.cxfrs.service.Endpoint" loggingFeatureEnabled="false" />
  <bean id="myCustomProcessor" class="nl.rubix.cxfrs.service.MyCustomProcessor"/>

  <camelContext trace="false" id="blueprintContext" xmlns="http://camel.apache.org/schema/blueprint">
    <route customId="true" id="cxfrs.service">
        <from uri="cxfrs:bean:rsServer?bindingStyle=Default"/>
        <bean ref="myCustomProcessor"/>
        <log message="The message contains ${body}"/>
        <marshal>
            <json library="Jackson"/>
        </marshal>
    </route>
</camelContext>

</blueprint>


The log statements now outputs: The message contains 1-bla

A more extensive example using the SimpleConsumer

As mentioned above when using a very simplistic call containing only two path parameters handling the CXFRS message is quite simple using Camel, regardless of the binding type used. However when dealing with more complex scenarios the SimpleConsumer definitely can save time and effort.

When we add a POST method to our Endpoint class and add the request message to the signature the SimpleConsumer maps the request body in the POST to the Camel message body.

The Endpoint class looks like this:


package nl.rubix.cxfrs.service;

import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/")
public class Endpoint {
    
    @GET
    @Path("/test/{id}/{id2}")
    @Produces(MediaType.APPLICATION_JSON)
    public String getAssets(@PathParam("id") String id, @PathParam("id2") String id2){
        return null;
    }

    @POST
    @Path("/test/{id}/{id2}")
    @Produces(MediaType.APPLICATION_JSON)
    public String doPost(String request, @PathParam("id") String id, @PathParam("id2") String id2){
        return null;
        
    }
}


Note the String request parameter in our doPost method.

Using SoapUI to send the POST request to the endpoint:

simple_vs_default_binding_2When we modify our Camel route to log both the body and headers we get the following result in our log:

INFO The message body contains [“test”,”request”]

INFO The message headers contains {id=1, CamelAcceptContentType=*/*, User-Agent=Apache-HttpClient/4.1.1 (java 1.5), connection=keep-alive, CamelHttpCharacterEncoding=ISO-8859-1, CamelCxfRsOperationResourceInfoStack=[org.apache.cxf.jaxrs.model.MethodInvocationInfo@2637df06], id2=bla, Host=localhost:2345, CamelCxfRsResponseClass=class java.lang.String, CamelHttpMethod=POST, CamelHttpUri=/testendpoint/test/1/bla, content-type=application/json, CamelCxfRsResponseGenericType=class java.lang.String, accept-encoding=gzip,deflate, operationName=doPost, breadcrumbId=ID-localhost-localdomain-37504-1423222735361-0-1, CamelHttpPath=/test/1/bla, Content-Length=18, CamelCxfMessage=org.apache.cxf.message.XMLMessage@319f9d1d}

As we can see the SimpleConsumer adds all http headers to the Camel headers, but also creates Camel exchange headers for the PathParam parameters we defined in our method signature (displayed in bold).

For reference our Camel route looks like this:


<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:camel="http://camel.apache.org/schema/blueprint"
       xmlns:cxf="http://camel.apache.org/schema/blueprint/cxf"
       xsi:schemaLocation="
       http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
       http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">

  <cxf:rsServer id="rsServer" address="http://localhost:2345/testendpoint" serviceClass="nl.rubix.cxfrs.service.Endpoint" loggingFeatureEnabled="false" />

  <camelContext trace="false" id="blueprintContext" xmlns="http://camel.apache.org/schema/blueprint">
    <route customId="true" id="cxfrs.service">
        <from uri="cxfrs:bean:rsServer?bindingStyle=SimpleConsumer"/>
        <log message="The message body contains ${body}"/>
        <log message="The message headers contains ${headers}"/>
        <marshal>
            <json library="Jackson"/>
        </marshal>
    </route>
</camelContext>

</blueprint>


The SimpleConsumer binding style provides a lot of ease when consuming REST messages from Camel and can be a real time saver. Especially when dealing with (even slightly) more complex REST messages.

A note on the Custom binding

Although the Custom binding is not covered in detail in this post the third bindingStyle option is to create a Custom binding and use this in your Camel Route. Creating a Custom binding is often not required, as the simple of default binding style showed above usually provide the desired functionality. However, the option does exist to create a custom binding. To implement a Custom binding create a new class implementing the org.apache.camel.component.cxf.jaxrs.CxfRsBinding interface. This class can be added to the Spring or Blueprint context by instantiating it as a bean. As a final step the custom binding can be used in the URI of the Camel CXFRS component by adding the bindingStyle=Custom&binding=#myBinding

 

 
Leave a comment

Posted by on 2015-02-06 in JBoss Fuse

 

Tags: , , , , ,