Introduction
The article assumes that the reader is using and has general knowledge of the ASP.NET AJAX framework.
Get it? Keeping your solution "clean" with "AJAX and SOAP"? Anyway, this article was originally titled "Super Simple JSON with ASP.NET AJAX", but I was being somewhat liberal with my use of the term "JSON" (though no more liberal than the way I have seen others use the term "AJAX"). The real title of the article should be something like "How to take advantage of a Web Service that returns JavaScript objects without having to worry about serialization or Web Services." Or maybe, it should be "Super Simple Serialization" to take advantage of alliteration. Or maybe even, "Calling Web Services from Client Script in ASP.NET AJAX" (though I think that one is already taken). The true reason for the current title will be revealed at the end of the article. In any case, this is the easiest way I have found to take advantage of AJAX without having to think about a Web Service layer and without having to worry about serializing or deserializing the objects that I am already using.
The following example uses JavaScript to call a method on its "host" page which will return a person object and display information about that person in a popup box. The example is contained in one ASPX page for simplicity.
Using the code
First, the code-behind
using System;
using System.Web.UI;
using System.Web.Services;
namespace AJAXExample
{
public partial class _Default : Page
{
public class Person
{
public Person(string name, DateTime birthDate, Person[] friends)
{
this.name = name;
this.birthDate = birthDate;
this.friends = friends;
}
private string name;
public string Name
{
get { return name; }
}
private DateTime birthDate;
public DateTime BirthDate
{
get { return birthDate; }
}
public int Age
{
get { return DateTime.Now.Year - BirthDate.Year; }
}
private Person[] friends;
public Person[] Friends
{
get { return friends; }
}
}
protected void Page_Load(object sender, EventArgs e)
{
}
[WebMethod]
public static Person GetPerson()
{
Person friend1 = new Person("friend one", DateTime.Now, null);
Person friend2 = new Person("friend two", DateTime.Now, null);
return new Person("person name", DateTime.Parse("7/4/1980"),
new Person[] { friend1, friend2 });
}
}
}
Explanation of the code-behind
The first thing you will notice is the definition of a Person
class. This class, of course, could be anywhere, and best practices would say that it is probably in its own file. Either way, things to notice about it are that all the properties are read only, one of the properties is a collection, and there is even a calculated field. The fields don't need to be read only, but the class has been designed that way to show that any class can be serialized into a JavaScript object.
The only method on the page is the GetPerson()
method which returns a new instance of a person when called. You will notice that the method is static
, public
, and marked with the WebMethod
attribute. This is all that is needed to make the method accessible to the JavaScript on the page. Calling this method will return a proxy object that highly resembles the Person
class described in the previous paragraph. Because the object is returned to its caller as a JavaScript object, the properties will no longer be read only, calculated fields will no longer have logic behind them, and all methods on the object will no longer be present but all of the data will persist in the same structure (hierarchy), even the properties that are collections.
Now, the ASPX page
<%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="Default.aspx.cs"
Inherits="AJAXExample._Default" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>AJAX Example</title>
<script type="text/javascript">
function GetPerson()
{
PageMethods.GetPerson(OnGetPersonSucceeded);
}
function OnGetPersonSucceeded(result, userContext, methodName)
{
alert("name: " + result.Name
+ "\r\nbirthdate: " + result.BirthDate
+ "\r\nage: " + result.Age + "\r\nfriends: "
+ result.Friends.length);
}
</script>
</head>
<body>
<form id="form" runat="server">
<asp:ScriptManager
ID="scriptManager"
runat="server"
EnablePageMethods="true">
</asp:ScriptManager>
<input type="button" onclick="GetPerson();" value="get person" />
</form>
</body>
</html>
Explanation of the ASPX page
The first thing to notice is that there is one, and only one, ScriptManager
on the page, and its EnablePageMethods
property is set to true
. Setting this property to true
is what allows the JavaScript on the page to "see" the static method we created in the code-behind. A PageMethods
object is then created by the ScriptManager
that allows access to the code-behind method. The only element on the page is an HTML button that calls the JavaScript function GetPerson()
. The JavaScript function GetPerson()
is really just a pass through method that uses the PageMethods
object to call the GetPerson()
method in the code-behind. PageMethod.GetPerson
accepts several parameters including callback functions for both success and failure. This implementation assumes success every time, and thus only passes in the OnGetPersonSucceeded
function as the first parameter. The Person
class from the GetPerson
method in the code-behind (the static Web Service method) is returned to the OnGetPersonSucceeded
as a JavaScript object. The first parameter of this method (result) is actually the serialized Person
object, and its properties have been populated with the data from the code-behind object.
Summary
That is all there is to it. No extensive JSON, serialization, or Web Service experience is necessary, just a ScriptManager
with its EnablePageMethods
property set to true
and a public, static method with a WebMethod
attribute. Here is the best part and the true reason for the title of this article, testing is easier now. JavaScript can be hard to code and debug, and even harder to test. The method described above allows for more logic to be moved to the code-behind, which in turn allows for the help of a compiler, and allows much easier access by testing software such as NUnit. I'm a big believer in keeping as much logic out of the UI as possible, and this seems like a great way to reduce some of that hairy JavaScript code and to keep a project "clean".
Resources