Codecamp BA - RESTful Services part 2

17.09.2010 13:52Comments
In the previous post I have explained what REST is and it's principles. In this post we'll see how we can make RESTful services in WCF WebHttp and how to implement each of the principles.

WCF WebHttp

WCF WebHttp is the new name for the System.ServiceModel.Web namespace where most of the support for services over HTTP is. As part of the WCF it relies in the usual constructs such as ServiceContract, DataContract, bindings, channels, etc. Since WCF WebHttp has been designed to work over HTTP, it gives us more control over the Uris, formats and the protocols based on HTTP than any other programming model inside WCF. While working on .NET 3.5, you will have to download the WCF REST Starter Kit Preview 2 if you want to build RESTful services. The starter kit comes with a lot of samples that show how to implement each of the principles, and even more! As for the .NET 4.0 we already have many of the classes of the starter kit in the System.Service.Model namespace. And we even have out of the box support for the UrlRoutingModule, in order to build Uri templates, pretty much like we build them in MVC. However if what you want is to develop a client of a REST service, then I recommend downloading and including the starter kit assemblies too, because we’ll find some very useful extensions in there. Now let’s look at a very simple example of a REST service that exposes contacts as resources. From visual studio 2010 you can download the “WCF REST Service Template 40 (CS)” extension and you’ll get a new project type in the create project dialog. The first thing you’ll notice from this project once you create one, is the lack of an .svc file. This is because we are using the UrlRoutingModule as you can see in the web.config file:
<modules runAllManagedModulesForAllRequests="true">
      <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</modules>
And the url you’ll have to type into the browser will depend on what has been registered in the Global.asax (just like in ASP .NET MVC).
private void RegisterRoutes()
{
// Edit the base address of Service1 by replacing the "Service1" string below
RouteTable.Routes.Add(new ServiceRoute("Service1", new WebServiceHostFactory(), typeof(ContactsService)));
}
As you’ll see, the service contract looks pretty much like any other service contract:
[ServiceContract]
interface IContactsService
{

[WebGet(UriTemplate = "")]
List<Contact> GetCollection();

[WebInvoke(UriTemplate = "", Method = "POST")]
Contact Create(Contact instance);

[WebGet(UriTemplate = "{id}")]
Contact Get(string id);

[WebInvoke(UriTemplate = "{id}", Method = "PUT")]
Contact Update(string id, Contact instance);

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

}
Except for these two attributes WebGet, and WebInvoke. The interesting part of these attributes are the UriTemplate and the Method. The UriTemplate defines the way the Uris will look. So for instance if you do a GET to our service you will get the collection of contacts. But if you perform a GET with “/anyid” then you will get the contact with id “anyid”. As for the Method is pretty simple, it specifies the HTTP verb that’s being requested over the resource. And nothing special about the Contact data contract:
public class Contact
    {
        public string Id { get;  set;}
        public string Name { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public string Location { get; set; }
    }
The service implementation doesn’t have anything special. You would have to implement this interface, and either retrieve the contacts, or update/delete/insert contacts into a repository. If we wanted to make good use of HTTP protocol, and somebody requested a resource that doesn't exist, then we should return a status code 404. We can accomplish that by throwing a WebFaultException like this:
        public Contact Get(string id)
        {
            var contact = new ContactRepository().Get(id);
            if (contact == null)
                throw new WebFaultException<string>("Contact not found.", System.Net.HttpStatusCode.NotFound);

            return contact;
        }
If we want to support multiple formats, WCF comes with an out of the box auto format selection option that can easily be enabled from the the web.config: And last as a bonus, the WCF team added some support for a help page which helps you describe the operations you have available in your service so that others can build their clients (REST does not support WSDL or anything like that). So if you look at your service model configuration you will see a new property: And thanks to this, if you browse http://server/myservice/help you’ll see a help page. Pretty much like this one:

REST Client

Now that we have the help page, we are ready to build our client application. Here you can download the WCF REST Starter Kit Preview 2 that comes with some very interesting extensions that make client development a lot easier. You'll have to include Microsoft.Http and Microsoft.Http.Extensions dlls to your project. We can use the client library to read the service response as a string like this:
using (HttpClient client = new HttpClient(SERVICE_URL))
            {
                // Getting the response as a string 
                client.DefaultHeaders.ContentType = "text/json";
                using (HttpResponseMessage response = client.Get("")) // no need for a url.
                {
                    System.Console.WriteLine("Getting all contacts");
                    response.EnsureStatusIsSuccessful();
                    var stringResponse = response.Content.ReadAsString();
                    System.Console.WriteLine(stringResponse);
                }
            }
Notice the use of the extension method ReadAsString(). You also have a ReadAsXElement extension method to query the response using Linq to XML; and my favorite extension method ReadAsDataContract that will deserialize the response using the DataContract Serializer and return an instance of T. Here's how we would query our Contacts service:
using (HttpClient client = new HttpClient(SERVICE_URL))
            {
                using (HttpResponseMessage response = client.Get(""))
                {
                    response.EnsureStatusIsSuccessful();
                    var contacts = response.Content.ReadAsDataContract<ContactList>();
                    foreach (var contact in contacts)
                        System.Console.WriteLine(string.Format("Name: {0}", contact.Name));
                }
            }
            System.Console.ReadKey();
And here are the DataContracts:
[CollectionDataContract(Namespace = "http://schemas.datacontract.org/2004/07/Contacts.Lib", Name="ArrayOfContact", ItemName="Contact")]
    public class ContactList : List<Contact> { }

    [DataContract(Namespace = "http://schemas.datacontract.org/2004/07/Contacts.Lib")]
    public partial class Contact
    {
        [DataMember]
        public string Id;
        [DataMember]
        public string Name;
        [DataMember]
        public string Email;
        [DataMember]
        public string LastName;
        [DataMember]
        public string Location;
        [DataMember]
        public string Ref;
    }
And for the client side bonus, you can wrap the GET, PUT, POST and DELETE into a base class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Http;
using System.Runtime.Serialization;
using System.Configuration;

namespace Contacts.Client
{
    public abstract class RestClient
    {
        private string url;

        public RestClient()
        {
            url = ConfigurationManager.AppSettings["ContactsSvcUrl"];
            if (string.IsNullOrEmpty(url))
                throw new ConfigurationErrorsException("ContactsSvcUrl application setting is null.");
        }

        public RestClient(string serviceUrl)
        {
            if (string.IsNullOrEmpty(serviceUrl))
                throw new ArgumentNullException("serviceUrl");

            this.url = serviceUrl;
        }

        protected T ExecuteGet<T>(string operation)
        {
            using (var client = new HttpClient(url))
            {
                using (HttpResponseMessage response = client.Get(operation))
                {
                    response.EnsureStatusIsSuccessful();
                    return response.Content.ReadAsDataContract<T>();
                }
            }
        }

        protected T ExecutePut<T>(string operation, T entity)
        {
            using (var client = new HttpClient(url))
            {
                HttpContent content = HttpContentExtensions.CreateDataContract(entity);
                using (HttpResponseMessage response = client.Put(operation, content))
                {
                    response.EnsureStatusIsSuccessful();
                    return response.Content.ReadAsDataContract<T>();
                }
            }
        }

        protected T ExecutePost<T>(string operation, T entity)
        {
            using (var client = new HttpClient(url))
            {
                HttpContent content = HttpContentExtensions.CreateDataContract(entity);
                using (HttpResponseMessage response = client.Post(operation, content))
                {
                    response.EnsureStatusIsSuccessful();
                    return response.Content.ReadAsDataContract<T>();
                }
            }
        }

        protected void ExecuteDelete(string operation)
        {
            using (var client = new HttpClient(url))
            {
                using (HttpResponseMessage response = client.Delete(operation))
                {
                    response.EnsureStatusIsSuccessful();
                }
            }
        }
    }
}
... so that we can define our Contacts proxy like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Http;

namespace Contacts.Client
{
    public class ContactsClient : RestClient
    {
        public ContactsClient(string serviceUrl) : base(serviceUrl)
        {
        }

        public ContactList GetAll()
        {
            return ExecuteGet<ContactList>("");
        }

        public Contact Get(string id)
        {
            return ExecuteGet<Contact>(id);
        }

        public Contact Update(Contact contact)
        {
            return ExecutePut<Contact>(contact.Id, contact);
        }

        public void Delete(string id)
        {
            ExecuteDelete(id);
        }

    }
}
Not so hard after all hu? So in this post we saw how to build our first REST service in which we expose basic CRUD operations for our Contacts; and then how to consume this service from our clients.

comments powered by Disqus