Implementing RESTiful WCF service
This post will give some simple insight on how to implement a RESTiful service with WCF.
REST stands for Representational State Transfer. Its an architectural style that focusses on accessing resources through a universal interface URI. An architectural style of software is something that describes the features that can be used to build a software. And REST is actually one way to implement client server architecture style and infact it builds on client-server archicture style. So a service that uses rest architectural style is usually reffered as RESTiful service or endpoint
Rest constraints are based on underlying principles that govern the web which includes:
- User agents interacts with resources and resources are anything that can be named and represented. Each resource can be addressed via unique URI
- The interaction with resouces is accomplished using uniform interface http stardand verbs. There is also declaration of the resource’s media type via http content-type header
- Resources are descriptive
- Resources contains links to other resources. (HATEOAS)
Even though WCF geared towards RPC (Remote Process Call) using SOAP, there is the capability to expose and consume REST services. The REST programming is around two attributes WebInvokeAttribute and WebGetAttribute and URI template mechanism that enable us to declare the URI and method verb.
WebHttpBinding binding and WebHttpBehavior provides the correct networking stack for using REST with WCF. The hosting infrastructure used to host RESTiful service are WebServiceHost and WebServiceHostFactory which extends ServiceHost and ServiceHostFactory respectively.
The WebGetAttribute tells the dispatcher that the method should only respond to HTTP GET request while WebInvokeAttribute which is mapped to HTTP POST by default, can be set to support any other HTTP VERB e.g PUT,DELETE
UriTemplate is used to enable customization of the URI for each method and verb combination.
So lets dive into some code to see this in action. The sample we are using is not a stardand just used to pass the concept of how to implement a RESTiful WCF service. Our data contract looks like
[DataContract] public class Person { [DataMember] public int Id { get; set; } [DataMember] public string Name { get; set; } [DataMember] public string Email { get; set; } [DataMember] public int Age { get; set; } }
And here is the contract declaration, and to note here is the usage of the WebGet and WebInvoke attributes. I would like to point out that if you look at the UriTemplate for GetPerson and DeletePerson looks the same, but they have the different HTTP VERB.
[ServiceContract(Namespace = "https://piusnjoka.wordpress.com/v1/2013/07/20")] public interface IPerson { [OperationContract] [WebInvoke(Method = "POST", UriTemplate = "save")] void AddPerson(Person person); [OperationContract] [WebInvoke(Method = "PUT", UriTemplate = "update")] void UpdatePerson(Person person); [OperationContract] [WebInvoke(Method = "DELETE", UriTemplate = "person/{id}")] void DeletePerson(string id); [OperationContract] [WebGet(UriTemplate = "persons")] List<Person> GetAllPerson(); [OperationContract] [WebGet(UriTemplate = "person/{id}")] Person GetPerson(string id); }
This is our service implementation, which is a singleton service.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] public class PersonService : IPerson { private static List<Person> persons = new List<Person>(); public void AddPerson(Person person) { persons.Add(person); } public void UpdatePerson(Person person) { //TODO: Implement bette update here DeletePerson(person.Id.ToString()); persons.Add(person); //do some update here } public void DeletePerson(string id) { persons.RemoveAll(x => x.Id == int.Parse(id)); } public List<Person> GetAllPerson() { return persons; } public Person GetPerson(string id) { return persons.FirstOrDefault(x => x.Id == int.Parse(id)); } }
And remember we said that we use WebHttpBinding to support REST and there in our app.config in host project you will find this
<system.serviceModel> <services> <service name="WCFRestSample.Service.PersonService"> <host> <baseAddresses> <add baseAddress="http://localhost:8082"/> </baseAddresses> </host> <endpoint address="" binding="webHttpBinding" contract="WCFRestSample.Service.IPerson"></endpoint> </service> </services> </system.serviceModel>
For simplicity, we will use a self hosting in a console application. Of importance to note here is the “WebServiceHost” which we are using to host our service. If in case it find that there is no endpoint configured in the service description it creates one at the service base address i.e for http/https. The WebServiceHost also add WebHttpBehavior to endpoint that do not have the behavior configured.
WebServiceHost host = new WebServiceHost(typeof(PersonService)); try { host.Open(); Console.WriteLine("Host Running"); Console.ReadKey(); host.Close(); } catch (Exception) { host.Abort(); } }
Thats it we have our service ready for hosting. Any client can call the RESTiful service and in our case we are going to use a simple console application which looks as below. And of importance to note is the “WebChannelFactory<IPerson>”. This adds WebHttpBehavior to endpoints if it does not already exist.
WebChannelFactory<IPerson> factory = new WebChannelFactory<IPerson>(new Uri("http://localhost:8082")); var channel = factory.CreateChannel(); //Add three persons Console.WriteLine("***Adding three initial persons**"); channel.AddPerson(new Person { Age = 12, Email = "testone@gmail.com", Id = 0, Name = "test user one" }); channel.AddPerson(new Person { Age = 12, Email = "testtwo@gmail.com", Id = 1, Name = "test user two" }); channel.AddPerson(new Person { Age = 12, Email = "testthree@gmail.com", Id = 2, Name = "test user three" }); Console.WriteLine("There {0} persons", channel.GetAllPerson().Count()); //Delete one Console.WriteLine("Deleting person with id on 0"); channel.DeletePerson("0"); Console.WriteLine("There {0} persons", channel.GetAllPerson().Count()); Console.Read(); Console.WriteLine("Updating person with Id of 1"); var toUpdate = channel.GetPerson("1"); channel.UpdatePerson(toUpdate); Console.WriteLine("Update done"); //Display to account Console.WriteLine(channel.GetAllPerson().Count()); Console.ReadKey();
To finish lets look at some of the advantages which come with REST.
Hope you enjoyed. Happy coding 🙂