How to host and consume WCF RESTful Services


There’s been a lot of jazz about RESTful services, which was published to the masses by a person named Roy Thomas Fielding in his PhD dissertation. Since then, RESTful services have been embraced and widely used. To name a few, Twitter and Facebook exposes RESTful web services API to us to consume. I shall not go into the principles and concepts of REST, but I personally find RESTful services to be very natural, easy to understand and it just flows with how the World Wide Web works, which is driven by the HTTP protocol. You can learn more about RESTful principles from wikipedia, or just google it. It’s very much the opposite of using SOAP, which has been the Microsoft way for some time now, but in .NET 3.5, REST has found its way into WCF.

Now back to business, let’s talk about how to create a RESTful service using WCF. If you search google, you will find heaps of examples how to do create WCF RESTful services, but there’s been very little information out there about how to consume it. I found some examples about using WCF ChannelFactory and generating a client proxy to consume the WCF RESTful service, but I feel these approaches violates the whole point of using REST. In order to consume a RESTful service, regardless of how it’s hosted, we should just consume it using simple HTTP requests generated from code. In this post, I will show you how to create, host and consume the services with WCF and the .NET framework.

Hosting a WCF RESTful Service

Let’s get straight into it. The code below shows my service contract for a fictitious Product service. Since REST is about defining resources, so in this scenario Product is our resource.

Using System.Servicemodel.Web;

[ServiceContract]
public interface IProduct
{
    [OperationContract]
    [WebGet(UriTemplate = "/")]
    Product[] GetAllProducts();

    [OperationContract]
    [WebGet(UriTemplate = "/{id}")]
    Product GetProductById(string id);

    [OperationContract]
    [WebInvoke(UriTemplate = "/create", Method = "POST")]//, RequestFormat=WebMessageFormat.Xml, BodyStyle=WebMessageBodyStyle.Bare)]
    void CreateProduct(Product product);

    [OperationContract]
    [WebInvoke(UriTemplate = "/{id}", Method = "PUT")]
    void UpdateProduct(string id, Product product);

    [OperationContract]
    [WebInvoke(UriTemplate = "/{id}", Method = "DELETE")]
    void DeleteProduct(string id);

}

As with all WCF Services, you will require ServiceContract and OperationContract. What makes this RESTful is the WebGet and WebInvoke attributes from the System.Servicemodel.Web namespace. WebGet defines the operation as a HTTP “GET”, and returns the response in the payload, either as XML or JSON. You can define your request and response format using the RequestFormat and ResponseFormat properties of the WebGet and WebInvoke Properties. Most important thing here to note is the UriTemplate property, which defines the format of the URL that will be used to consume the web service. For instance, “http://localhost/Products.svc/” will call the GetAllProducts service operation, and “http://localhost/Products.svc/create” will call the CreateProduct service operation. Notice the use of curly brackets {} in the UriTemplate, which directly maps to your service operation’s input parameters. To get a specific product with id = 5, URL would look like so…”http://localhost/Products.svc/5″.

WebGet specifies an operation that is triggered using a HTTP “GET”. WebInvoke on the other hand specifies either a HTTP “POST”, “PUT” or “DELETE” which is defined via the Method property, hence we can use the same UriTemplate but only if they are defined using different HTTP methods.

Moving on, here’s a snippet of the service implementation.

public class Products : IProduct
{
    private static List
<product> products = new List</product>
<product>();

    static Products()
    {
        products.AddRange(new Product[] { new Product { Id="1", Name = "Product1", Description = "Product Description1" },
                                        new Product { Id="2", Name = "Product2", Description = "Product Description2" },
                                        new Product { Id="3", Name = "Product3", Description = "Product Description3" } });
    }

    public Product[] GetAllProducts()
    {
        return products.ToArray();
    }

    public Product GetProductById(string id)
    {
        Product found = products.Find(p => p.Id.Equals(id));
        if (found != null)
        {
            return found;
        }
        WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound("Resource not found");
        return null;
    }

    public void CreateProduct(Product product)
    {
        product.Id = (products.Count + 1).ToString();
        products.Add(product);
        WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.Created;
    }

    public void UpdateProduct(string id, Product product)
    {
        Product found = products.Find(p => p.Id.Equals(id));
        if (found != null)
        {
            found.Name = product.Name;
            found.Description = product.Description;
        }
        else
        {
            WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound("Resource not found");
        }

    }

    public void DeleteProduct(string id)
    {
        Product found = products.Find(p => p.Id.Equals(id));
        if (found != null)
        {
            products.Remove(found);
        }
        else
        {
            WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound("Resource not found");
        }
    } 

}

Nothing fancy going on here, but good thing to know is the WebOperationContext class. You can use this class to access the IncomingRequest, OutgoingRequest, IncomingResponse and OutgoingResponse properties to access various useful aspects of the HTTP request and response. For instance, you can use the SetStatusAsNotFound method to return a HTTP response with status code 404 to the client when the resource being requested cannot not found.

Below is a snippet of the Product class, which is decorated as a DataContract.

/// <summary>
/// Namespace = "" is important to note when you structure your request payload during consumption of the web service. If you specify
/// a namespace, you need to use the same namespace when creating your XML payload. In this example, none is required because we are
/// omitting the namespace.
/// </summary>
[DataContract(Namespace = "")]
public class Product
{
    [DataMember]
    public string Id { get; set; }

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

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

Important thing to note here is the use of DataContract(Namespace=””)]. This is important when you are trying to consume the service because you won’t be able to do a “POST”, “PUT” or “DELETE” if the namespace of the XML in the payload and the one on your DataContract does not match. Therefore, I recommend you set a Namespace explicitly to either something you define or just omit it (if you dont set this explicitly, one is automatically generated). Lastly, I decided to omit the WCF configuration in Web.Config, by using another approach. In Product.svc file, I just used the following line.

< %@ ServiceHost Language="C#" Factory="System.ServiceModel.Activation.WebServiceHostFactory"  Service="RestfulService.Products"  %>

By using WebServiceHostFactory, you can omit the WCF configuration because it’s automatically generated for you with default settings. If you need something other than the default settings, then you would need to specify a configuation in Web.Config. I will leave it to the reader to find out how to do this, using webHttpBinding and webHttpBehavior.

Now start the service up in your favorite browser, and this is what you should get.

restfulbrowser

Consuming RESTful Services

To structure a simple HTTP request to a RESTful service, you would need to use WebRequest and WebResponse classes in the System.Net namespace. These are abstract classes, but you can still play with them using some static methods. However if you need more control, you will need to cast them to their derived counterparts, HttpWebRequest and HttpWebResponse classes. Here’s some sample code on how to do a HTTP “GET” for all products in this context.

WebRequest req = WebRequest.Create(@"http://localhost:61447/Products.svc/");

req.Method = "GET";

HttpWebResponse resp = req.GetResponse() as HttpWebResponse;
if (resp.StatusCode == HttpStatusCode.OK)
{
    using (Stream respStream = resp.GetResponseStream())
    {
        StreamReader reader = new StreamReader(respStream, Encoding.UTF8);
        Console.WriteLine(reader.ReadToEnd());
    }
}
else
{
    Console.WriteLine(string.Format("Status Code: {0}, Status Description: {1}", resp.StatusCode, resp.StatusDescription));
}
Console.Read();

Here’s a sample on how to do a HTTP “POST” to create a new product.

class Program
{
    static void Main(string[] args)
    {
        // change the port number accordingly for this to work, when your cassini starts up.
        WebRequest req = WebRequest.Create(@"http://localhost:61447/Products.svc/create");

        req.Method = "POST";
        req.ContentType = @"application/xml; charset=utf-8";
        WriteProductXml(req, "new name", "new description");

        HttpWebResponse resp = req.GetResponse() as HttpWebResponse;
        Console.WriteLine(string.Format("Status Code: {0}, Status Description: {1}", resp.StatusCode, resp.StatusDescription));
        Console.Read();
    }

    public static void WriteProductXml(WebRequest req, string name, string description)
    {
        StringBuilder builder = new StringBuilder();
        builder.AppendLine("</product>
<product>");
        builder.AppendLine("<description>" + description + "</description>");
        builder.AppendLine("<id></id>");
        builder.AppendLine("<name>" + name + "</name>");
        builder.AppendLine("</product>");

        req.ContentLength = Encoding.UTF8.GetByteCount(builder.ToString());

        using (Stream stream = req.GetRequestStream())
        {
            stream.Write(Encoding.UTF8.GetBytes(builder.ToString()), 0, Encoding.UTF8.GetByteCount(builder.ToString()));
        }
    }
}

Take note that you will need to define the Method, ContentType and ContentLength for HTTP “POST” to work. I’ve used a simple StringBuilder to build my Product XML, but you can use any other approach, e.g. System.Xml.XmlDocument or System.Linq.Xml.XDocument. Lastly you will need to write the Product XML to the RequestStream payload. Remember I mentioned about XML namespace of the Product class, this is very important because if the namespace of the client and service don’t match, the WCF RESTful service won’t be able to deserialize the Product object.

I won’t go into implementations for HTTP “PUT” and “DELETE” to update and delete a product, they’re essentially the same as the “POST”. If you want to see the implementation, download the sample solution as the end of this post.

How to use the sample solution

In the sample solution, there’s a WCF project, and 4 Console Application projects. The 4 console applications perform GET, UPDATE, DELETE and CREATE. What I suggest is to first build the solution (to start the Cassini web server), then view the WCF service in a browser by right-clicking the Product.svc and “View in browser”. Once you have done that and the browser loads up the service, you can trigger the CREATE, UPDATE and DELETE using the corresponding console applications by right-clicking on the console application, and selecting “Debug as new instance”. To reload the new results, you can refresh the browser using Ctrl+F5 (so that it does not refresh from browser cache).

viewinbrowserdebugnewinstance

I hope this sample will be useful, especially the portion about how to consume RESTful services.
You can download the sample solution here. Happy coding!

Share this post:
Advertisements
Posted in WCF. Tags: . 5 Comments »

5 Responses to “How to host and consume WCF RESTful Services”

  1. SOA 2.0: SOA evolved « Code Blitz Says:

    […] only use WSDualHttpBinding to create this callback mechanism over HTTP. I suppose this rules out WCF RESTful services as a means to support […]

  2. JD Says:

    Thank you so much for providing not only the sample code but also ‘How to use the sample solution’. For someone who’s learning WCF for the first time it’s so useful and many others omit the simple steps which can confuse! Worked first time and has given me a concrete example I can run in Cassini to experiment with as I learn.

  3. Bannu Says:

    Thanks a lot this helped me a lot and i am trying to do this by connecting to data base (sql server) but in the web app and output in json format at line 22 (HttpWebResponse resp = req.GetResponse() as HttpWebResponse;) an error occured saying webexception unhandled, the remote server returned an error 404.
    How can i solve this…?

  4. sharmoon Says:

    thanks a lot. this is helpful.

  5. Rk Says:

    Nice article..

    But I have found another simple example to call a WCF RestFul Service using jQuery AJAX Call, please refer to link below:-
    http://www.etechpulse.com/2014/03/consume-restful-wcf-service-in-aspnet.html
    http://www.etechpulse.com/2014/03/how-to-consume-wcf-restful-service-in.html

    Thanks


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: