Wednesday, March 12, 2008

REST with WCF

At the MS Heroes day, a few thursdays ago, our first session was on WCF and is fair to say a few of us were very impressed by what I saw and I just couldn't wait to recreate those presentations on my desk..

What is WCF?

From MSDN,

Is a set of .net Technologies for building and running connected systems. It is a new breed of communications infrastructure built around the Web services architecture. Advanced Web services support in Windows Communication Foundation provides secure, reliable, and transacted messaging along with interoperability. The service-oriented programming model of Windows Communication Foundation is built on the Microsoft .NET Framework and simplifies development of connected systems

 

What is REST ?

REST is a architectural model ( it is not a standard ) for distributed computing that defines how resources are distributed and addressed.

The acronym means Representational State Transfer, translated to mere mortals language it means that each unique URL is a representation of some data. It was term invented by Roy Fielding one of the principal authors of the HTTP protocol.

I will post resources and links later in the post. However Google for REST and you'll get heaps of results if you are curious. Definitions are boring! I like doing things. But you need to know a bit of the theory.

eg:

http://localhost/myWebService/Employee/2

http://localhost/myWebService/Employee

http://localhost/myWebService/Product/89

REST WebServices: An alternative to SOAP

All the above urls will return you different data ( a representation of the resource requested ) according to (of course) on how you map this URL to your contracts.  First link might be mapped to return you the Employee with ID =2 and so on, or the Product with ID = 89

Basic principles

  1. Identify the entities you want to expose a representation eg Employee, Product
  2. Create URL for each resource/entity. Resources should be nouns.
  3. Categorise resources according to whether clients can just get a representation of your data or whether they can modify the data. For resources that you just want to make available to clients, make them accessible using HTTP GET, for resources that you want to give the client the possibility to modify data, make them accessible using HTTP POST, PUT or DELETE
  4. Resources accessible via GET should be 'side effect' free. This means the web service call should not modify the data in the back end.

Yahoo and Flickr amongst others use REST web services. Example of Flickr REST web service can be seen here

Main advantages of using REST

  • Easy to work with, and easy to read for 'humans'.
  • Easy to consume ( not always, I hear Amazon services can be a pain to parse )
  • Improved server response times.

 

I started working with this on the weekend after Microsoft Heroes in Sydney. At the time I challenged myself not to Google for answers and get the examples working only with the help of MSDN for WCF.

It was not easy at first, MSDN is huge and it throws so much information at you, if you are not careful you start losing track of what link you open where and soon you lose track of what you are reading, at least I do!

Needless to say it wasn't that easy. I find it interesting to see how much developers including myself sometimes google problems searching for tailor-made answers. Imagine a world without Google? Is mindblowing to think how older programmers looked for answers 20 years ago, maybe things where simpler those days. Hrmm..

Ok, so lets get started, first of all I went NewSite -> WCF Service. Visual Studio will create a simple solution with an IService and a Service class and also a Service.svc file.

First of all lets look at the IService interface is provided by default.

[ServiceContract]
public interface IService
{

    [OperationContract]
    string GetData(int value);

    [OperationContract]
    CompositeType GetDataUsingDataContract(CompositeType composite);

    // TODO: Add your service operations here
}

FIrst thing you do is define a contract. You do this by adding the [ServiceContract]  attribute to your interface.  This contract will define what operations your webservice will ultimately support. Think of an operation as a method call that your service will expose. Each method in the interface that you want to expose, must then have the [OperationContract]  attribute defined / applied to it. If a method does not have this attribute, it won't be exposed therefore it wont be accessible by the service's clients.

We then have to implement this interface and implement each method that we defined in our ServiceContract.

I want to expose a method by a REST WebService to return an Employee Name representation when the URL is "GetEmployeeName/{id}".

Remember that if we just want a representation of our data without any side effect by the REST principles we have to use the HTTP GET verb. WCF Web model allows you to do this by using the WebGet method attribute for Get operations. All you have to do is adorn your method with WebGet.

For that to happen I have to define  my operation contract in the interface as follows:

[OperationContract]
[WebGet(UriTemplate= "GetEmployeeName/{id}", ResponseFormat = WebMessageFormat.Xml)]
stringGetEmployeeName(Stringid);

In the WebGet attribute we can also describe the layout of the URL that our WebService will respond to. For example I want the method above to be called upon to answer the GetEmployeeFirstName call when the URL is MyService.svc/GetEmployeeName/2

We do this by adding a UriTemplate property to our WebGet attribute. This templates allow you to match an incoming URL to an operation in the service. The ResponseFormat attribute is self explanatory, you can choose XML or JSON.

Now we have to implement our GetEmployeeeFirstName method in our class.

public class Service : IService
{

    public string GetEmployeeName(string id)
    {
        NorthwindDataContext ct = new NorthwindDataContext();
        return ct.Employees.Where(a => a.EmployeeID == Int32.Parse(id)).Select(a => a.FirstName + " " + a.LastName).Single();
    }
}

I'm using LinqToSql again to retrieve employees from the Northwind database.

We are almost ready to go now. I had my biggest struggle with the web.config and configuring behaviours and endpoints. I wasn't the only one that struggled, for what I can gather around blogs etc and the poor MSDN documentation this wasn't that straightforward.

I settled for the config settings below...

<system.serviceModel>
        <bindings>
            <webHttpBinding>
                <binding name="ServiceBehavior"></binding>                
            </webHttpBinding>        
        </bindings>
        <services>
            <service name="Service" behaviorConfiguration="ServiceBehavior">
                <!-- Service Endpoints -->
                <endpoint address=""  binding="webHttpBinding" behaviorConfiguration="ServiceBehavior" contract="IService"></endpoint>
                <endpoint contract="IService" address="mex" binding="mexHttpBinding"></endpoint>                
            </service>            
        </services>
        <behaviors>
            <endpointBehaviors>
                <behavior  name="ServiceBehavior">
                    <webHttp />                     
                </behavior>                
            </endpointBehaviors>
            <serviceBehaviors>
                <behavior name="ServiceBehavior">
                     To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment 
                    <serviceMetadata httpGetEnabled="true" />
                                     To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information 
                    <serviceDebug includeExceptionDetailInFaults="true"/>
                </behavior>  
            </serviceBehaviors>
       </behaviors>
</system.serviceModel>

Since I'm using the WCF web model and all my requests are through HTTP and not SOAP I have to use webHttpBinding .

From MSDN..

The WCF Web Programming Model allows developers to expose WCF Web services through HTTP requests that use "plain old XML" (POX) style messaging instead of SOAP-based messaging. For clients to communicate with a service using HTTP requests, an endpoint of the service must be configured with the WebHttpBinding that has the WebHttpBehavior attached to it. The WCF Web Programming Model also requires that the individual service operations are annotated with the WebGetAttribute or WebInvokeAttribute attributes. This defines a mapping from a URI and HTTP method to the service operation, as well as the format of the messages used to call the operation and return the results. Support in WCF for syndication and ASP.AJAX integration are both built on top of the WCF Web Programming Model.

SO far so good, Now if we run our WCF app and we invoke the URL with GetEmployeeName/2 the employee name should be returned...

http://localhost/DemoWCF/Service.svc/GetEmployeeName/2

Capture

http://localhost/DemoWCF/Service.svc/GetEmployeeName/3

Capture2

(BTW Vista Snipping Tool kicks bum big time!!!) ( David, I got to talk you into Vista for blogging Windows Live Writer Full, is awesome)

Lets say I know want to get all the Employees Names by going to an url like this

http://localhost/DemoWCF/Service.svc/Employees

[OperationContract]
[WebGet(UriTemplate = "Employees", ResponseFormat = WebMessageFormat.Xml)]
List<String> GetEmployeesNames();

My implementation of the operation would be something along the lines of..

public List<String> GetEmployeesNames()
{
    NorthwindDataContext ct = new NorthwindDataContext();
    Enumerable<String> ab = from emps in ct.Employees
                            select emps.FirstName + " " + emps.LastName;
    return ab.ToList();
}

And the result would be..

Capture3 

Now if I wanted the Response to be of

WebMessageFormat.Json

I would then have to change my OperationContract to note my intention as follows

[OperationContract]
[WebGet(UriTemplate = "Employees", ResponseFormat = WebMessageFormat.Json)]
List<String> GetEmployeesNames();

When I then go to the URI, you will then get prompted to save the response as a file...

image 

that upon opening with trusty Notepad you can see the JSON string...

Capture5 

In this post I just wanted to show how I got my REST services to work and I then plan to post about how to use PUT, POST and DELETE , how to return complex types, DataContracts and also how to pass querystring values to your OperationContract as a Stream, that in fact was the first thing that I got working. ( Quick preview below). I would also like to post all the source code of my working examples.

[OperationContract]
[WebInvoke(UriTemplate = "*", Method = "POST")]
void UpdateEmployeeName(Stream id);
Implementation
public void UpdateEmployeeName(Stream id)
{
   StreamReader reader = new StreamReader(id);
   String res = reader.ReadToEnd();
   NameValueCollection coll = HttpUtility.ParseQueryString(res);
   int employeeID = Int32.Parse(coll["elid"]);
   String newName = coll["newName"];
   NorthwindDataContext cts = new NorthwindDataContext();
   Employee e = cts.Employees.Where(a => a.EmployeeID == employeeID ).Single();
   e.LastName = newName;
   cts.SubmitChanges();
}
See you then.........

2 comments:

David Tchepak said...

I do use Vista and Live Writer! Except at work if posting over lunch :)

Although I've been tempted to switch to Windows Server 2008 or back to the latest Ubuntu :)

Anonymous said...

Who knows where to download XRumer 5.0 Palladium?
Help, please. All recommend this program to effectively advertise on the Internet, this is the best program!