Build a REST Web Application with WCF and jQuery

Wednesday, February 24, 2010

Introduction

So at my work, we started on a new web application, and mainly because a lot of guys working on it were going to be new and also because it is somewhat a visible project; we figured we would keep the risk low and use the traditional web forms ASP.Net application.

On the other hand we still wanted the separation of concerns you get from a MVC style application, and because we were also planming on using designers for the web pages who were very proficient with jQuery but who did not know much about ASP.net. As a matter of fact, most of these guys write jQuery and HTML with a simple text editor…show offs :)

Also, for performance reasons and the fact the web designers were very familiar with the protocol, we wanted to pass JSON objects back and forth from the client to the server and vice versa.

Another goal is we want to keep as little as code as possible in the web project and place it in a different “Core” assembly. This would make deployments for us easier.

The Solution Layout

The solution looks like this:

The EventList.aspx page will be the page I will use for this demo. I deleted the associated code-behind files with and also removed the references of the code-behind from the page deplaration.

When I added the EventService.svc WCF Service into the project, Visual Studio added the EventService.svc.cs file and the IEventService service contract interface. I moved them to the Core project and changed the namespaces.

I also added a folder for the Unity service factory build up. You can see how I did it in a previous post.

The Service

Since I moved the associated files that were added when I added the WCF Service, I have to go in and change the declarations so they point to the proper place.

   1: <%@ ServiceHost 
   2:     Language="C#" 
   3:     Debug="true" 
   4:     Service="RestSample.Core.ServiceContracts.EventsService" 
   5:     Factory="RestSample.Core.ServiceContrainer.UnityServiceHostFactory" 
   6: %>

 

Also the code behind reference was moved and Factory attribute was added to point to my custom Unity Service Host Factory.

The Service Contract

The Service contract needs to be decorated with the ServiceContract attrubute; but I also need to specify that this message will take “POST” requests, and that the protocal will be JSON.

   1: using System.ServiceModel;
   2: using System.ServiceModel.Web;
   3: using RestSample.Core.DataContracts;
   4:  
   5: namespace RestSample.Core.ServiceContracts
   6: {
   7:     [ServiceContract]
   8:     public interface IEventsService
   9:     {
  10:         [OperationContract]
  11:         [WebInvoke(Method = "POST",
  12:             ResponseFormat = WebMessageFormat.Json,
  13:             BodyStyle = WebMessageBodyStyle.Bare)]
  14:         EventList GetLatestEvents();
  15:     }
  16: }

 

The implantation of this contract is injected with my controller where my response will be built up.

Here is the service contract implementation:

   1: using RestSample.Core.Controllers;
   2: using RestSample.Core.DataContracts;
   3:  
   4: namespace RestSample.Core.ServiceContracts
   5: {
   6:     public class EventsService : IEventsService
   7:     {
   8:         private readonly IEventController _eventController;
   9:  
  10:         public EventsService(IEventController eventController)
  11:         {
  12:             _eventController = eventController;
  13:         }
  14:  
  15:         #region IEventsService Members
  16:  
  17:         public EventList GetLatestEvents()
  18:         {
  19:             return _eventController.GetLatestEvents();
  20:         }
  21:  
  22:         #endregion
  23:     }
  24: }

 

Here is the controller class implementation:

   1: using System;
   2: using System.Collections.Generic;
   3: using RestSample.Core.DataContracts;
   4:  
   5: namespace RestSample.Core.Controllers
   6: {
   7:     public class EventController : IEventController
   8:     {
   9:         #region IEventController Members
  10:  
  11:         public EventList GetLatestEvents()
  12:         {
  13:             var list = new EventList {Items = new List<EventItem>()};
  14:             const int upper = 5;
  15:             for (int i = 0; i < upper; i++)
  16:             {
  17:                 list.Items.Add(new EventItem
  18:                                    {
  19:                                        EventCode = i.ToString(),
  20:                                        EventDate = DateTime.Today.AddDays(-1*i).ToLongDateString(),
  21:                                        EventName = string.Format("Name {0}", i)
  22:                                    });
  23:             }
  24:  
  25:             return list;
  26:         }
  27:  
  28:         #endregion
  29:     }
  30: }

Obviously I am just returning back trash, but you get the point. Here are the data contracts:

   1: using System.Runtime.Serialization;
   2:  
   3: namespace RestSample.Core.DataContracts
   4: {
   5:     [DataContract]
   6:     public class EventItem
   7:     {
   8:         [DataMember]
   9:         public string EventCode { get; set; }
  10:         [DataMember]
  11:         public string EventName { get; set; }
  12:         [DataMember]
  13:         public string EventDate { get; set; }
  14:     }
  15: }

 

   1: using System.Collections.Generic;
   2: using System.Runtime.Serialization;
   3:  
   4: namespace RestSample.Core.DataContracts
   5: {
   6:     [DataContract]
   7:     public class EventList
   8:     {
   9:         [DataMember]
  10:         public IList<EventItem> Items { get; set; }
  11:     }
  12: }

 

The web.config Set Up.

Because I want to return JSON, and because I moved the service files; I need to make some changes to the System.ServiceModel section of the web.config.

   1: <system.serviceModel>
   2:     <behaviors>
   3:         <serviceBehaviors>
   4:             <behavior name="RestSample.Core.ServiceContracts.EventsServiceBehavior">
   5:                 <serviceMetadata httpGetEnabled="true"/>
   6:                 <serviceDebug includeExceptionDetailInFaults="false"/>
   7:             </behavior>
   8:         </serviceBehaviors>
   9:   <endpointBehaviors>
  10:     <behavior name="WebHttpBehavior">
  11:       <webHttp />
  12:     </behavior>
  13:   </endpointBehaviors>
  14:     </behaviors>
  15:     <services>
  16:         <service behaviorConfiguration="RestSample.Core.ServiceContracts.EventsServiceBehavior" name="RestSample.Core.ServiceContracts.EventsService">
  17:             <endpoint address="" binding="webHttpBinding" contract="RestSample.Core.ServiceContracts.IEventsService" behaviorConfiguration="WebHttpBehavior">
  18:                 <identity>
  19:                     <dns value="localhost"/>
  20:                 </identity>
  21:             </endpoint>
  22:             <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
  23:         </service>
  24:     </services>
  25: </system.serviceModel>

I added an Endpoint Behavior and specified webHttp and then referenced it in the endpoint. This enables the “web programming model” for WCF which makes the service RESTful.

I also changed the name and contract location of the service so it points to new location in my solution.

Now my service is ready and if I run the service file at this point I get the standard service page to come up to show the everything is configured properly.

The Client

Now that the service is working I am just going to write some jQuery to make a call to the service when a button is click.

Here is the aspx page.

   1: <%@ Page Title="" Language="C#" MasterPageFile="~/Shared/Main.Master" %>
   2: <asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
   3:     <script src="../Scripts/json2.js" type="text/javascript"></script>
   4:     <script src="../Scripts/restsample.js" type="text/javascript"></script>
   5: </asp:Content>
   6: <asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
   7:     
   8:     <input type="button" value="Make Call" id="restCall" />
   9: </asp:Content>

Here is the jQuery

   1:  
   2: $(document).ready(function() {
   3:     setInputFunction();
   4: });
   5:  
   6: function setInputFunction() {
   7:     $('input').click(function() {
   8:         makeEventCall();
   9:     });
  10: }
  11:  
  12: function makeEventCall() {
  13:     $.ajax({
  14:         url: 'http://localhost:56296/Services/EventsService.svc/GetLatestEvents',
  15:         type: "POST",
  16:         processData: false,
  17:         contentType: "application/json",
  18:         timeout: 10000,
  19:         dataType: "text",  // not "json" we'll parse
  20:         success: function(data) {
  21:             loadSuccessful(data);
  22:         }
  23:     });
  24: }
  25:  
  26: function loadSuccessful(data) {
  27:     var result = JSON.parse(data);
  28:     console.log('result', result);
  29: }
  30:  
  31:  

So the jQuery code above makes a call to the service when the button is clicked. Here are some things to notice about this jQuery code.

  • First off, you cannot see it here but I have a script reference to the jQuery 1.4.1 file located on the Microsoft CDN. More on that here.
  • The url is the a combination of the service url and the function GetLatestEvents.
  • I need to specify “POST” as the type because that is what I specified on my ServiceContract.
  • I am going to return back text and then parse it using Douglas Crockford's json2.js.
  • For this demo, I am just going to log the response to the Firebug console using the console.log feature. (Note: remember to take this out later because you will get a JavaScript error in other browsers).

So with I am done and now when I bring up that web page and click the button I get this JSON response back.

{"Items":[{"EventCode":"0","EventDate":"Saturday, February 20, 2010","EventName":"Name 0"},
{"EventCode":"1","EventDate":"Friday, February 19, 2010","EventName":"Name 1"},
{"EventCode":"2","EventDate":"Thursday, February 18, 2010","EventName":"Name 2"},
{"EventCode":"3","EventDate":"Wednesday, February 17, 2010","EventName":"Name 3"},
{"EventCode":"4","EventDate":"Tuesday, February 16, 2010","EventName":"Name 4"}]}

The response in Firebug looks like this:

response

 

Hope that helps :)

comments powered by Disqus