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.