Tuesday, March 17, 2009

ASP.NET Web Services, JSON, and jQuery

I've recent found jQuery as a very powerful tool. It simplifies a lot of how the DOM is traversed. Additionally, it makes writing JavaScript more consistent and bringing a lot of what newer languages contain right from the get go. With jQuery you can really build powerful web applications without having to spend hours writing JavaScript to do some very trivial operations.

One of many things that jQuery simplifies is making AJAX calls. Invoke call to the “ajax” method, wire up a delegate to handle various response types and presto--instant Ajax. These calls are especially easy when arguments and return values are of basic data types (string, int, float, etc). Passing in the data attribute a JSON representation of the parameters on the web service (e.g. { parameterName: parameterValue} ) . Though, sometimes it is necessary to pass complex data types to a web service. Here I’ll illustrate how to do this.

Below I've create some very simple page level web services. Services defined within a pages scope are automatically ScriptServices and do not require the denotation. I'll explain more on ScriptServices later.

[WebMethod()]
public static List<Post> GetPosts()
{
List<Post> posts = new List<Post>();
posts.Add(new Post("JSON Rules!", "Some body text", "Johnnie Beam"));
posts.Add(new Post(".NET Rocks!", "More body text", "Jim Daniels"));
posts.Add(new Post("jQuery Pwns!", "Some more body text", "Jack Walker"));

return posts;
}

[WebMethod()]
public static string CreatePost(Post post)
{
return "server received: " + post.Title;
}

The GetPosts method returns a list of Post objects. While CreatePost takes a Post object as an argument, simply returning a string. The Post class for these objects is identified below.


public class Post
{
public string Title { get; set; }
public string Body { get; set; }
public string Author { get; set; }

public Post(string title, string body, string author)
{
this.Title = title;
this.Body = body;
this.Author = author;
}

public Post() { }
}

One thing to note is, I've explicitly identified an empty, zero argument constructor. Reason that this is necessary is due to the way .NET handles the service request. It takes the data and creates a new instances of all arguments and assigns values appropriately (this process is known as Deserialization).

Now that we have our sample services, it's time that we invoke them. Below I've created a sample that will do just that.

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>WebService Sample</title>
<script type="text/javascript" language="javascript" src="scripts/jquery-1.3.2.js"></script>
<script type="text/javascript" language="javascript" src="scripts/json2.js"></script>
<script type="text/javascript" language="javascript">
$("document").ready(function() {
// wire up save click
$("#save").click(function() {
Save($("#title").val(), $("#body").val(), $("#author").val());
});

// load listings from server
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "webservice.aspx/GetPosts",
data: "{ }",
dataType: "json",
success: function(msg) {
$(msg.d).each(function() {
var newElement = $("<div></div>");
newElement.append("<h3>" + this.Title + "</h3>");
newElement.append("<p>" + this.Body + "</p>");

$("#postListing").append(newElement);
});
}
});
});

function Save(title, body, author) {
var parameters = { post: { Title: title, Body: body, Author: author} };

$.ajax({
type: "POST",
url: "webservice.aspx/CreatePost",
data: JSON.stringify(parameters),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(msg) {
$("#result").text(msg.d);
}
});
}
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<span id="result"></span>

<div>
<h2>Create Post</h2>
<span>Title: </span><input type="text" id="title" /><br />
<span>Body: </span><textarea id="body" rows="3" cols="30" ></textarea><br />
<span>Author: </span><input type="text" id="author" /><br />
<button id="save">Save</button>
</div>

<div id="postListing">
<h2>Post Listing</h2>
</div>
</div>
</form>
</body>
</html>

Before I explain what is going on here, let’s take a little bit of a dive into ScriptServices. ASP.NET AJAX introduces the concept of ScriptServices. ScriptServices can be called in a much more simplistic manner, as compared to SOAP services. SOAP Services contain a large amount of data that is expressed in XML. Making SOAP very verbose and impacting overall performance. ScriptServices are an attempt at making web service more RESTful. REST services streamline the process by using the existing HTTP infrastructure, rather than complicated SOAP messages; typically using JSON as data format. There is a lot that can be said on this topic--I won't go into that discussion here.

Back to the sample--Once the DOM is ready we setup the "Save" click handler and then make a call to the GetPosts method. Here we signify that the resulting data type will be a JSON object. This is possible because we've identified the content type of the request to be "application/json; charset=utf-8". This tells ASP.NET, this message needs to be handled as a ScriptService.

When the call successfully completes, the JSON object from the web service is passed to the Success handler (as a note the JSON object is actually wrapped in a "d" object. My assumption here is that this is done for future compatibility. Potentially the ScriptService, in the future, may pass along various information from the server.) Here we simply create a new DIV elements for all items returned, displaying the title and body.

When the user clicks on the Save button, the values of the various input fields are passed into the Save function. The save function first creates a JSON object that identifies the parameters to be sent back to the ScriptService. Then an ajax call is made. This call looks very similar to the GetPosts call, however, the data property is very much different. Here we perform a stringify on the parameters object. The stringify call takes a JSON object and turns into its string representation.

NOTE: stringify method comes from the json2.js file. More information on this can be found here.

TIP: A technique that I use when I'm not sure of the data being passed to the success event, I will stringify the msg.d object and pass that into an alert. This provides a good way of visualizing what the object looks like.

alert(JSON.stringify(msg.d));

Here we've demonstrated that you can take a complex data type and bring it down from a service, additionally, you can build a JSON object from JavaScript and send it into a .NET ScriptService. This paradigm makes web 2.0 style development effortless.

No comments:

Post a Comment