Anatomy of a simple REST client

In Anatomy of a simple REST service we created a service using WFC Web HTTP services. The service has a single resource that supports the GET and POST verbs. Let’s create a client for this REST service.

Creating a client application

Start with the previous solution (see the download at the end) open in Visual Studio, and create a new Console Application: in the Solution Explorer, right-click on the solution node and select Add | New Project | Visual C# | Windows | Console Application and name it AcmePaintClient.

Creating a client DTO

The Colors service from the previous solution has the Color resource’s GET send, and POST receive Color objects. However, they are JSON object on the wire, so on the client side we are free to create any DTO we want so long as it’s compatible.with the JSON object. Compatibility include having properties whose names match the names of the fields in the JSON object. Compatibility does not include having the same class name as was using in the server DTO or preserving the order of the properties..

Add a class named AcmeColor and add properties whose names match the names of the Color DTO defined in the previous solution, like this.

public class AcmeColor
{
  public byte Green { get; set; }
  public byte Blue { get; set; }
  public byte Red { get; set; }
}

Creating a client

First, let’s change the service URL to be a fixed value. In Solution Explorer | AcmePaint double-click on the Properties item. On the AcmePaint Properties window, select the Web tab. Under Servers, verify that Use Visual Studio Development Server is selected, and select Specific port. Note the value of the port, we’ll be using that shortly. My value is 42524.

Next, we’ll add a reference to an assembly we’ll be using. In Solution Explorer, right-click on the AcmePaintClient project and select Add Reference. On the .NET tab, select System.Runtime.Serialization.

In the Program.cs file, add the following using declarations and the GetColor method.

using System;
using System.Net;
using System.Runtime.Serialization.Json;

private static AcmeColor GetColor( int id )
{
  using ( var client = new WebClient() )
  {
    var uri = new Uri(
      string.Format( "http://localhost:42524/Colors.svc/Color/{0}", id ) );

    using ( var stream = client.OpenRead( uri ) )
    {
      var serializer = new DataContractJsonSerializer( typeof( AcmeColor ) );
      return (AcmeColor)serializer.ReadObject( stream );
    }
  }
}

Quite simply, this is using the WebClient.OpenRead method (== open then read) to perform the GET operation, which returns a Stream that contains the returned JSON. Then the DataContractJsonSerialicer.ReadObject method reads the JSON stream into an AcmeColor type.

Also in the Program.cs file, add the following PostColor method.

private static void PostColor( AcmeColor color )
{
  using ( var client = new WebClient() )
  {
    client.Headers[ HttpRequestHeader.ContentType ] = "application/json";

    var uri = new Uri( "http://localhost:42524/Colors.svc/Color" );
    using ( var stream = client.OpenWrite( uri, "POST" ) )
    {
      var serializer = new DataContractJsonSerializer( typeof( AcmeColor ) );
      serializer.WriteObject( stream, color );
      stream.Close();
    }
  }
}

This time we’re using the WebClient.OpenWrite method (== open then write) to perform the POST operation. This method also returns a Stream, but this time it is a stream for us to write the JSON from an AcmeColor type. That task is performed by DataContractJsonSerializer.WriteObject.

Also note that we’ve set the HTTP Content-Type header so that the service knows what it is receiving.

Finally, modify the Main method to call both of our methods.

static void Main( string[] args )
{
  var color = GetColor( 3 );

  Console.WriteLine( "Color: Red={0}, Green={1}, Blue={2}",
    color.Red, color.Green, color.Blue );

  PostColor( new AcmeColor { Red = 0x80, Green = 0xff, Blue = 0x80 } );

  Console.WriteLine( "Press any key to exit..." );
  Console.ReadKey();
}

Running the client

In the Solution Explorer, right-click on the AcmePaintClient project and select Set as StartUp Project. Then press F5 to start with debugging. The following output is displayed:

Color: Red=255, Green=128, Blue=0
Press any key to exit...

Debugging the service

So far you have no real evidence that the PostColor method actually communicated with the Colors.svc service. I did that expressly so that we can also explore how to debug the service, which a very handy thing to know.

Here’s a simple technique based on the following observation. An instance of Visual Studio can only debug a single program at a time; however it can start as many programs as you like without debugging.

Let’s add a pause at the very top of the Program.Main method so that we can control when it starts making service calls.

Console.WriteLine( "Press any key to start..." );
Console.ReadKey();

Next, in the AcmePaint project, open Color.svc.cs (double-click on the Color.svc item) and set a breakpoint on the close curly-brace for the PostColor method, e.g., by clicking in the left column next to that line in the source code, or by setting the cursor on that line and pressing F9.

Now, with AcmePaintClient still set as the start-up project, press Ctrl+F5 to start running the client without debugging.

Return to Visual Studio, and in Solution Explorer, right-click on AcmePaint project and select Set as StartUp Project. Then press F5 to start the service with debugging.

Return to the console window running the AcmePaintClient and press any key so that it starts making calls to the service.

Visual Studio should hit the breakpoint at the end of the PostColor method. View the value of the requestedColor variable in the Locals window (if the Locals window is not displayed select Debug | Windows | Locals.) You can verify that it is the requestedColor value is the same value that was sent by the client.

Cleaning up

In Visual Studio, press F5 to let the service invocation run to completion. In the AcmePaintClient console window, press any key to exit. In Visual Studio, press Shift+F5 to stop debugging the AcmePaint service.

Review

We created a minimal REST service client using the WebClient class to perform HTTP requests for both the GET and POST verbs. We also learned a technique for debugging and stepping into the service.

Resources

Download the completed code

Additional Posts in this series

Anatomy of a simple REST service

Anatomy of a simple REST service

I want to discuss some interesting topics about services, notably service versioning. Let me start by looking at a very simple REST service using WCF Web HTTP services, creating one to explore how it is put together.

Creating a service application

Start by creating a new WCF Service Application project in Visual Studio with File | New | Project | Visual C# | WCF | WCF Service Application and name it AcmePaint. In the Solution Explorer, delete the generated Service1.svc and IService1.cs files.

Creating a DTO

Add a class named Color. This class is a Data Transfer Object (DTO) whose singular goal should be to represent the service’s message as it is transferred from one process to another. This class is responsible for the shape of “the message on the wire.”

public class Color
{
  public byte Red { get; set; }
  public byte Green { get; set; }
  public byte Blue { get; set; }
}

Adding a WCF service

Right-click on the project in the Solution Explorer and select Add | New Item | Visual C# | Web | WCF Service and name it Colors.svc.

Writing the service contract

The service contract is expressed as an the interface IColors with the [ServiceContract] attribute; that part hasn’t changed since WCF was introduced. Edit the IColors interface to define two operations like this.

using System.ServiceModel;
using System.ServiceModel.Web;

namespace AcmePaint
{
  [ServiceContract]
  public interface IColors
  {
    [WebGet( UriTemplate = "Color/{id}" )]
    Color GetColor( string id );

    [WebInvoke( Method = "POST", UriTemplate = "Color" )]
    void PostColor( Color color );
  }
}

The GetColor method is our archetypal method for performing an HTTP request with the GET verb. The [WebGet] attribute tells the WCF infrastructure that this method is invoked using GET, and the UriTemplate named parameter declares the structure of the URL. The id parameter is written in curly braces which declares that its value in the URL will be passed to the method each time the method is invoked.

The PostColor method is our archetypal method invoked using an HTTP request with the POST verb as declared with the [WebInvoke( Method = “POST” )] attribute. Here UriTemplate declares that it uses the same resource name, Color, as the GetColor method, just without any following URL parts.

Note that both methods use the Color class for the resource sent (POST) and received (GET) by the service’s client.

Writing the service implementation

Edit the Colors class in Colors.svc.cs which implements the IColors interface. You can get to this file by double-clicking on the Colors.svc entry in the Solution Explorer. I am going to be focusing on getting data in and out of a service, so the implementations here are intentionally bare bones.

namespace AcmePaint
{
  public class Colors : IColors
  {
    public Color GetColor( string id )
    {
      // retrieve the color from its id here...

      return new Color { Red = 0xff, Green = 0x80, Blue = 0x00 };
    }

    public void PostColor( Color color )
    {
      Color requestedColor = color; // illustrate the type of color parameter

      // do something with color here...
    }
  }
}

Inspecting the .svc file

In the Solution Explorer, right-click on Colors.svc and select View Markup. You should see that it s a one-liner containing the following.

<%@ ServiceHost Language="C#" Debug="true" Service="AcmePaint.Colors" CodeBehind="Colors.svc.cs" %>

Aside from declaring the implementation language and enabling debugging, this file associates a service name, AcmePaint.Colors, with a source code file containing the class that implements the service. We’ll see that service name again in our next step.

Configuring the service

Open the Web.config file and replace its contents with the following.

<?xml version="1.0"?>
<configuration>

  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>

  <system.serviceModel>

    <services>
      <service name="AcmePaint.Colors">
        <endpoint address=""
                  contract="AcmePaint.IColors"
                  kind="webHttpEndpoint"/>
      </service>
    </services>

    <standardEndpoints>
      <webHttpEndpoint>
        <standardEndpoint name=""
                          helpEnabled="true"
                          defaultOutgoingResponseFormat="Json"/>
      </webHttpEndpoint>
    </standardEndpoints>

  </system.serviceModel>

</configuration>

The <service> element declares the service name using the same value we saw in the Colors.svc file.

Inside of that element we declare a service endpoint. The address attribute is empty which means that the endpoint address will just be the address of the Colors.svc service. The contract attribute names the interface that is our service contract. And the kind attribute declares this endpoint to be a standard web HTTP endpoint, which uses a fixed webHttpBinding. That is a binding configures endpoints to respond to HTTP requests instead of SOAP messages.

The standardEndpoint element has an empty name, making this the default for all webHttpEndpoints. We’ve used this default to enable help and to set the default format for outgoing response messages to be JSON..

Selecting hosting options

In the AcmePaint project in the Solution Explorer, double-click on the Properties item to display the AcmePaint properties window. Select the Web tab. In the Servers section, verify that Use Visual Studio Development Server is selected.

(If you have IIS installed on your local workstation, you could use the option to Use Local IIS Web Server instead.)

Running the service

In the Solution Explorer, select the AcmePaint project, then press F5 to run the project in debug mode.

Selecting a node in the Solution Explorer first is important! Otherwise, e.g., if you have the Colors.svc.cs file open and selected (active), then pressing F5 to run will launch the WCF Test Client which will display an error because this service is not exposing metadata.

Be patient for a moment while the ASP.NET Development Server starts up, and then your browser will launch a page with the URL http://localhost:xxxx/ where xxxx is the auto-assigned port number that was listed on the AcmeProject Properties page where you selected to use the Visual Studio Development Server.

restservicedirectorylisting

In the browser address bar, edit the URL to read http://localhost:xxxx/Colors.svc/help, using the same port number for xxxx as before. This is the help page that we enabled in configuration.

restservicehelppage

One last time edit the URL in the browser address bar, this time to read http://localhost:xxxx/Colors.svc/Color/42, with xxxx as before. This time the browser prompts whether you was to Open, Save or Cancel the response. Select Open. In the Open With dialog, select Notepad | OK. In the Open File dialog select Open. The following JSON is displayed in Notepad

{"Blue":0,"Green":128,"Red":255}

Cleaning up

Close the browser to stop debugging the service.

Review

We created a minimal REST service and used a browser as an impromptu client to GET a resource, with the response data being delivered as JSON.

Resources

Download the completed code

Serializing a DataContract class to an XML string

Sometimes during development I want to have a light-weight approach to logging request and response messages without setting up WCF tracing.​ Regardless of how I am logging (Trace, EntLib, whatever), I need to get a printable version of the data message.

I always recommend treating DataContract classes strictly as data transfer objects (DTO), that is they exist purely for describing the shape of the message on the wire. That means I don’t add any fancy constructors or business logic of any kind to the DTOs. This maintains a separation of concerns. However, all CLR types do have a ToString method, so I an fine with putting it to better use than telling me the name of the type.

In the sample code, CreateSecretRequest overrides ToString using a static helper method, Helper.ToXmlString. That helper method wraps an XmlSerializer and a StringWriter together to serialize the DTO to a string of XML that I can log. All DataContract classes can use the same ToXmlString method to implement their override of ToString in a simple one-liner.

using System.IO;
using System.Xml.Serialization; 

public static class Helper
{
  public static string ToXmlString( object value, string defaultNamespace )
  {
    var writer = new StringWriter();
    (new XmlSerializer( value.GetType(), defaultNamespace )).Serialize( writer, value );
    return writer.ToString();
  }
} 

// a sample data transfer object

using System.Runtime.Serialization; 

[DataContract( Namespace = XmlNamespaces.Secret.CreateSecretRequest )]
public class CreateSecretRequest
{
  [DataMember]
  public string Name { get; set; } 

  [DataMember]
  public string Description { get; set; } 

  public override string ToString()
  { return Helper.ToXmlString( this, XmlNamespaces.Secret.CreateSecretRequest ); }
}

Be careful! This is not the same XML that the service sent or received on the wire. Use WCF Tracing to see what is really happening with your services. This is nearly a 100% genuine approximation of the message, suitable for some quick-and-dirty logging or debugging diagnostics.

Reversing something that doesn’t belong to you

I was working on an implementation of a REST service recently, making it respond to the HTTP Accept headers on the incoming request. As you can see from the HTTP/1.1 specification, Accept request-headers should be honored in the order that they’re listed. So it was absolutely awesome to learn that

using System.ServiceModel.Web;

var acceptHeaders = WebOperationContext.Current.IncomingRequest.GetAcceptHeaderElements();
foreach ( var acceptHeader in acceptHeaders )
{
  // process acceptHeader...
}

processes the headers in the opposite order in which they are specified in the request. And that’s not awesome in a good sense.

The IncomingWebRequestContext.GetAcceptHeaderElements method returns a System.Collections.ObjectModel.Collection<T> and that is not a class I own or can change. But still I’d like an iterator for Collection<T> that yields up its elements in reverse order so that I can still use the foreach syntax. Since Collection<T> is not a class I can modify, extension methods come to mind. Here’s an extension method for an iterator on Collection<T> that does the trick.

using System.Collections.Generic;
using System.Collections.ObjectModel;

public static class Extensions
{
  public static IEnumerable<T> Reverse<T>( this Collection<T> collection )
  {
    for ( int i = collection.Count - 1; i >= 0; --i )
      yield return collection[ i ];
  }
}

Now I can write the foreach loop to use the extension method.

foreach ( var acceptHeader in acceptHeaders.Reverse() )
{
  // process acceptHeader in the correct order...
}

I am still dumbfounded that GetAcceptHeaderElements reverses the order of the elements (why would that ever be good?) but a few lines sends me on my way without cluttering up the code.