ASP.NET PageMethods with jQuery, JSON and Templates

The ASP.NET PageMethod, which employs the use of the [WebMethod] attribute from the System.Web.Services namespace, is not only very easy to use, it can also be a big time-saver. This article and demo will illustrate the use of the PageMethod to retrieve a list of current stock quotes from Yahoo finance, parse them into a List of .NET Quote objects, and return this result to the calling page as legal JSON.

The PageMethods feature of ASP.NET enables the use of codebehind page methods from the client, just like making a WebService call. This allows for the use of the handy jQuery $ajax method, and by specifying the  correct Content-Type and other features, one can build a usable WebService with as many methods as we want by using a simple ASP.NET Webforms Page. In fact, the client-side script and the server-side methods can both exist in a single ASPX page that "calls itself".

Setting up an ASP.NET page with a page method is simple.  You only need to:

1) Decorate the method with the [WebMethod] attribute
2) Mark the method as static.

Because these methods must be static, there is no viewstate and no postback mechanism with PageMethods; Page_Load and most of the rest of the ASP.NET page pipeline and events are eliminated, making PageMethods very efficient. In addition, with the use of PageMethods, our service-side classes do not need to be attributed with [DataContract], [DataMember] etc.

Here is my basic Quote class that represents a Yahoo stock quote:

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

namespace jQueryPageMethods
{

    public class Quote
    {
         public string Symbol { get; set; }
        public string Name { get; set; }
        public string LastTrade { get; set; }
         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 constructor accepts a string array of items to parse; this is how the data comes back from Yahoo finance. Now here is the actual Default.aspx page codebehind class:

using System;
using System.Collections.Generic;
using System.Json;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Services;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace jQueryPageMethods
{
    public partial class Default : System.Web.UI.Page
    {
         protected void Page_Load(object sender, EventArgs e)
         {

        }

        private static readonly string url1 = "http://download.finance.yahoo.com/d/quotes.csv?s=";
        private static readonly string url2 = "&f=snkc6";

        [WebMethod]
         public static List<Quote> GetData(string symbols)
        {
            var symbolString = symbols;
            var wc = new WebClient();
            string stuff = wc.DownloadString(url1 + symbolString + url2);
            wc.Dispose();
            string[] lines = stuff.Split("\r\n".ToCharArray());
            var quotes = new List<Quote>();
            foreach (string l in lines)
             {
                 if (l.Length < 2) continue;
                 // let's get rid of those quotation marks from Yahoo Finance
                string tmp = l.Replace('"', ' ');
                string[] toParse = tmp.Split(",".ToCharArray());
                var q = new Quote(toParse);
                 quotes.Add(q);
            }
               return quotes;
        }
    }
}

Notice above that everything we need here is marked static.  The PageMethod itself, of course, must be public, or we won't be able to see it. We accept a string of space-delimited stock symbols, make a WebClient DownloadString call, split the resulting string, and create a Quote object from each valid line, adding it to List<Quote> collection. This list is what is returned from the method.

The magic that makes this all happen is the client-side code, which makes the POST and tells the page that we want JSON:

<script type="text/javascript">
     $(document).ready(function () {
         $("#submit").click(function () {
             var symbols = $("#sym").val();
             $.ajax({
                 type: "POST",                
                   url: "./Default.aspx/GetData",  
                   data: "{'symbols': '"+ symbols+"'}", //note the additional quotation marks!
                   contentType: "application/json; charset=utf-8",                  
                 dataType: "json",
                 cache: false,
                 success: AjaxSucceeded,
                 error: AjaxFailed
             });
         });
     });

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

     function BuildTable(msg) {
         $("#quotes").empty();

         $('#quotesTemplate').tmpl(msg).appendTo('#quotes');
     }
    </script>


Note how the url is formed: pagename.aspx/Methodname. Also, note how the POST data is declared in the $ajax call. It takes the form:

data: "{'paramname': '"+ paramvalue +"'}",

If you do not get this right, you will simply get errors like "invalid JSON primitive". We want the browser to send this data as valid JSON, not as name=value form post pairs (the default).


The rest of the code is jQuery template code to build the display table. Once the data comes back, it should look like this:



You can download the complete Visual Studio 2010 Solution here.

By Peter Bromberg   Popularity  (6981 Views)