Stock Quotes via jQuery-enabled WCF Service, JSON, and jQuery Templates

One of the nice new features of Visual Studio 2010 is the new "Add item" template called "jQuery-Enabled WCF Service". This adds a new WCF Service to your project with just about everything pre-wired up to accept JSON data from a client page, and return legal JSON back. The client consumer page can then capture the JSON Data using jQuery and render the result with jQuery Templates. Even the originating call can be made via the jQuery $ajax method.

The Service Template even sticks in a Default.htm page with  a couple of sample jQuery calls - although it will require some serious modifications if you want to do anything really cool.

NOTE:  If you cannot find "jQuery-Enabled WCF Service", Install WCF support for jQuery if you do not have it:   http://wcf.codeplex.com/

This demo shows how to create a POHTML (plain old HTML) client page using jQuery and jQuery Templates to consume one of these services. We'll POST a string of stock symbols that you've entered with a button click, to the service's Post method, and it will go out to Yahoo finance, retrieve the quotes data (which is not JSON, just delimited csv-style text), massage them into C# Quote instances, convert the resulting List<Quote> collection to legal JSON, and send it back, whereupon the page will display the stock information in an HTML table via a JQuery Template.
  
The first thing to look at is the web.config file, which will contain a couple of new elements that are important if you want to be able to use a service url like   http://localhost:8326/.

  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
      <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
    </modules>
  </system.webServer>

  <system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>

  The UrlRoutingModule is used to create the route, which is instantiated in Global.asax:

  void Application_Start(object sender, EventArgs e)
         {
             // Code that runs on application startup

             RegisterRoutes();
        }

        private void RegisterRoutes()
        {
            // Edit the base address of the service by replacing the "Service" string below
            RouteTable.Routes.Add(new ServiceRoute("Service", new WebServiceHostFactory3(), typeof(WcfJQueryService)));
        }

Here is the actual Service class:

using System;
using System.Collections.Generic;
using System.Json;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using Microsoft.ServiceModel.Web;

namespace WCFjQueryServiceApplication1
{
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    [ServiceContract]
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class WcfJQueryService
    {
       
        private const string url1 = "http://download.finance.yahoo.com/d/quotes.csv?s=";
        private const string url2 = "&f=snkc6";
        
        [WebInvoke(UriTemplate = "", Method = "POST")]
        public JsonValue Post(JsonObject body)
        {
            dynamic item = body["symbols"];
            var symbolString = item.ToString();
            var wc = new WebClient();
            string stuff = wc.DownloadString(url1 + symbolString + url2);
            string[] lines = stuff.Split("\r\n".ToCharArray());
            var quotes = new List<Quote>();
            foreach (string l in lines)
             {
                 // let's get rid of those quotation marks
                string tmp = l.Replace('"', ' ');
                string[] toParse = tmp.Split(",".ToCharArray());
                var q = new Quote(toParse);
                 quotes.Add(q);
            }
            JsonValue retval = JsonObject.CreateFrom(quotes);
             return retval;
        }
    }
}

And my Quote class follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Web;

namespace WCFjQueryServiceApplication1
{
    [DataContract]
    public class Quote
    {
         [DataMember]
        public string Symbol { get; set; }
        [DataMember]
        public string Name { get; set; }
         [DataMember]
        public string LastTrade { get; set; }
         [DataMember]
        public string PercentChange { get; set; }
         public Quote(string[] toParse)
        {     
             try
            {
                 this.Symbol = toParse[0] ?? toParse[0];
                 this.Name = toParse[1] ?? toParse[1];
                 this.LastTrade = toParse[2] ?? toParse[2];
                 this.PercentChange =  toParse[3] ?? toParse[3];
             }
             catch
             {
             }
         }
    }
}

Note that  the Quote class must be properly attributed in order for serialization to work. What I'm doing above in the ctor is to take the Yahoo finance delimited string, turning it into a string array, and turn it into a Quote instance. Then the JsonObject.CreateFrom method is used to convert the List<Quote> collection to legal JSON.


Finally, this is what the client html page looks like:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>WcfJQueryService test</title>
     <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
  <script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>  
  <script id="quotesTemplate" type="text/x-jquery-tmpl">      
   <table cellspacing="2" cellpadding="2" border="0" width="50%" align="center" >
    <tr style="background-color:#CCFFFF">
      <td> ${Symbol}</td>
      <td>${Name} </td>
      <td>${LastTrade} </td>
      <td>${PercentChange} </td>
  </tr>
</table>
</script>
<script type="text/javascript">
        $(document).ready(function () {
            $("#submit").click(function (event) {
                var symbols = $("#sym").val()
                $.ajax({
                    type: "POST",
                    url: "./Service/",
                    data: { symbols: symbols },
                    cache: false,
                    success: AjaxSucceeded,
                    error: AjaxFailed
                });
            });
        });

        function AjaxSucceeded(result) {  
                BuildTable(result);                
            }
            function AjaxFailed(result) {
                $('#quotes').html(result.status + ' ' + result.statusText);
            }

            function BuildTable(msg) {
                $("#quotes").empty();
                
                $('#quotesTemplate').tmpl(msg).appendTo('#quotes');
            }
    </script>
</head>
<body>
<div>Enter quotes, space-separated</div>
<input type=text id=sym value="MSFT YHOO VZ GOOG" size=100 />
<input type=button value ="Get Quotes" id=submit />
<div id=quotes >  
</div>
</body>
</html>

This is all pretty standard stuff.  If you need more background, you can look at a previous article here, or view the jQuery documentation.

Download the Visual Studio 2010 Solution here.

By Peter Bromberg   Popularity  (6449 Views)