O'Reilly logo

REST in Practice by Savas Parastatidis, Jim Webber, Ian Robinson

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Consuming CRUD Services

Services are one side of distributed systems, but to perform useful work they need consumers to drive them through their protocols. Fortunately, many frameworks and libraries support CRUD Web Services, and it’s worthwhile to understand a little about what they offer.

A Java-Based Consumer

In the Java world, we might use the Apache Commons HTTP client[36] to implement the Create part of the protocol by POSTing an order to the ordering service, as shown in Example 4-31.

Example 4-31. Client-side order creation in Java

  public String placeOrder(Order order, String restbucksOrderingServiceUri)
                                throws BadRequestException, ServerFailureException,
                                       HttpException, IOException {

  PostMethod post = new PostMethod(restbucksOrderingServiceUri);
  // Use an existing XStream instance to generate XML for the order to transmit
  RequestEntity entity = new ByteArrayRequestEntity(
                                xstream.toXML(order).getBytes());
  post.setRequestEntity(entity);

  HttpClient client = new HttpClient();

  try {
    int response = client.executeMethod(post);

    if(response == 201) {
      return post.getResponseHeader("Location").getValue();
    } else if(response == 400) {
      // If we get a 400 response, the caller's gone wrong
      throw new BadRequestException();
    } else if(response == 500 || response == 503) {
      // If we get a 5xx response, the caller may retry
      throw new ServerFailureException(post.getResponseHeader("Retry-After"));
    }
    // Otherwise abandon the interaction
    throw new HttpException("Failed to create order. Status code: " + response);
  } finally {
    post.releaseConnection();
  }
}

The implementation in Example 4-31 shows the construction of a POST operation on the ordering service, using a PostMethod object. All we need to do is to populate the HTTP request with the necessary coffee order information by setting the request entity to contain the bytes of an XML representation of the order. To keep things simple for ourselves, we use the XStream library to encode the order resource representation in XML.

Having populated the HTTP request, we instantiate an HttpClient and execute the PostMethod, which POSTs the order to the Restbucks ordering service. Once the method returns, we examine the response code for a 201 Created status and return the contents of the Location header, which will contain the URI of the newly created order. We can use this URI in subsequent interactions with Restbucks. If we don’t get a 201 response, we fail by throwing an HTTPException, and assume that order creation has failed.

A .NET Consumer

On the .NET platform, we can opt for the framework’s built-in XML and HTTP libraries. The code in Example 4-32 represents how a client can send an order update to the Restbucks ordering service via HTTP PUT.

Example 4-32. .NET client code for order update via PUT

public void UpdateOrder(Order order, string orderUri)
{
  HttpWebRequest request = WebRequest.Create(orderUri) as HttpWebRequest;
  request.Method = "PUT";
  request.ContentType = "application/xml";

  XmlSerializer xmlSerializer = new XmlSerializer(typeof(Order));
  xmlSerializer.Serialize(request.GetRequestStream(), order);

  request.GetRequestStream().Close();

  HttpWebResponse response = (HttpWebResponse)request.GetResponse();

  if (response.StatusCode != HttpStatusCode.OK)
  {
    // Compensation logic omitted for brevity
  }
}

In Example 4-32, we use an HTTPWebRequest instance to handle the HTTP aspects of the interaction. First we set the HTTP verb PUT via the Method property and subsequently set the Content-Type header to application/xml through the ContentType property. We then write an XML-serialized representation of the order object that was given as an argument to the UpdateOrder() method. The XmlSerializer transforms the local object instance into an XML document, and the Serialize() method writes the XML to the request’s stream. Once we’re done populating the request stream, we simply call Close(). Under the covers, the framework sets other headers such as Content-Length and Host for us, so we don’t have to worry about them.

To send the request we call the GetResponse() method on the request object, which has the effect of transmitting an HTTP PUT to the URI supplied as an argument to the updateOrder() method. The response from the ordering service is returned as an HttpWebResponse and its StatusCode property triggers any further processing.

One final job that we need to undertake is to mark up the Order type so that the XmlSerializer knows how to transform Order instances to and from XML representations. The code snippet in Example 4-33 shows the .NET attributes that we need to apply for our client-side plumbing to be complete.

Example 4-33. An XML-serializable order

 [XmlRoot(Namespace = "http://schemas.restbucks.com/order")]
 [XmlType(TypeName = "order")]
 public class Order
 {

 [XmlElement(ElementName = "location")]
 public Location ConsumeLocation
 {
   get; set;
 }
 // Remainder of type omitted for brevity
}

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required