Showing posts with label AJAX. Show all posts
Showing posts with label AJAX. Show all posts

Wednesday, March 25, 2009

jQuery, JSON, and ASMX 2.0 Services

A few weeks ago, I had a project that was grounded in the .Net Framework v2.0 and Visual Studio 2005.  The requirements were very focused on usability and speed so AJAX and jQuery were high on my list to use.  Through the project, I learned that there isn't a large amount of information in one place that tells you how to setup a ASP.Net solution that uses jQuery and ASMX services to effectively transmit json data back and forth from the client.  Because of this, I'll attempt to fill this void since there are still many developers and companies out there that have not been able to upgrade to Visual Studio 2008.

In this post, I'll discuss the process of building a plain ASP.Net 2.0 web application project (not web site project) , setting up the necessary entries in the web.config file to utilize the ASP.Net 2.0 AJAX Extensions v1.0, and use jQuery at the client side of the transfers.    In addition, I'll also show to to use the JavaScriptSerializer class and how to write your own custom converter for your objects.

 

A Note About This Post:

This post is focused on Visual Studio 2005 with the ASP.Net 2.0 AJAX Extensions v1.0.  The v3.5 of the extensions that came with ASP.Net v3.5 and Visual Studio 2008 have a few changes.  While I will try to point out the differences, the core of this post is going to be focused on Visual Studio 2005 web application projects and v1.0 of the AJAX extensions.  While we all love focusing on the latest and greatest, I'm aware that there are a large number of companies and professionals out there that are locked into using the older version of the software for a number of reasons.

 

Additions to VS2005 Used in This Post:

This post will be using the following additions to VS2005.  Below are the items and links to their respective installers:

 

Code Downloads for This Post:

 

Creating and Configuring a New WAP:

Now that we have the required additions established, we're ready to create a new ASP.Net 2.0 Web Application Project.  I'm not going to get into the details of how to do this; however, I want to stress that I'm NOT choosing an ASP.Net AJAX Enabled Web Application.  I'm just choosing to create a new, basic ASP.Net Web Application Project.  Now that the project has been created, we need to add a few references and add some information into the Web.Config file.

In the Solution Explorer, we'll need to add a reference to the AJAX Extensions v1.0 library, System.Web.Extensions.dll.  By default, this library is located at C:\Program Files\Microsoft ASP.Net\ASP.Net 2.0 AJAX Extensions\v1.0.61025\ directory.  After adding this reference, we can update our Web.Config file by including a reference to the ScriptHandlerFactory HttpHandler using the following snippet inside of the <System.Web> config section:

   1: <remove verb="*" path="*.asmx"/>
   2: <add verb="*" 
   3:      path="*.asmx" 
   4:      validate="false" 
   5:      type="System.Web.Script.Services.ScriptHandlerFactory, 
   6:            System.Web.Extensions, 
   7:            Version=1.0.61025.0, 
   8:            Culture=neutral, 
   9:            PublicKeyToken=31bf3856ad364e35"/>

By adding the AJAX Extensions reference and updating the web.config file, we're now ready to enable our ASP.Net Web Services (ASMX) to be called from JavaScript.

 

Setting Up an ASMX Web Service:

We have our new WAP setup and configured, it's time to write a simple ASMX web service and configure it so that it will be able to return JSON.  To do this, let's add a web service to our project called JsonService.asmx and add the following code snippet to replace some of the defaults Visual Studio gives you:

   1: using System.Collections.Generic;
   2: using System.Web.Script.Services;
   3: using System.Web.Script.Serialization;
   4:  
   5: [WebService(Namespace = "http://YourNamespaceHere.com")]
   6: [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
   7: [ScriptService()]
   8: public class JsonService : System.Web.Services.WebService
   9: {
  10:     [WebMethod]
  11:     [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
  12:     public string GetCustomer()
  13:     {
  14:         // Method Body
  15:     }
  16: }

Lines 1-3, I imported 3 additional namespaces to use in the code.  System.Collections.Generics will be used in the next section.  System.Web.Script.Services allow us to decorate the service and it's methods as Script methods for the ScriptHandlerFactory to use when making AJAX calls to and from the client.

Line 7 decorates the web service with the [ScriptService()] attribute.

Line 11 decorates the GetCustomer() web method with the [ScriptMethod()] attribute.  This attribute tells the ScriptHandlerFactory that this method is allowed to be called from an Ajax Client.  The properties inside of the attribute, ResponseFormat = ResponseFormat.Json, tells the ScriptHandlerFactory to send the response stream as a json string and not XML or Soap.  If a response to the web service that is not formatted as json, the response will be returned as XML.

At this point, we can create the body of our web method in any fashion as long as it returns a string.  If you are only passing base types, you can skip down to the Talking to the Server Using jQuery section; however, if you want to pass something a bit more complex, I recommend you continue to the next section.

 

Using the JavaScriptSerializer and a Custom Converter:

While passing base types is easy enough, it can be important to pass objects back and forth from the client.  In order to assist with this, the ASP.Net AJAX Extensions v1.0 comes with the JavaScriptSerializer object.  This object has the ability to serialize certain objects into strings representing json objects.  This sounds great and the answer to all of our problem!  Too bad it is very limiting in it's natural state.  It CAN convert Arrays of base types and (from what I can tell) any framework classes that implements IEnumerable<T>.  I haven't experimented with some of the more obscure generic collections; however, I do know that it does concern List<T> and Dictionary<S,V> just fine. 

In order to use the JavaScriptSerializer, you simply instantiate it and call its Serialize() method, passing the object that you wish to serialize.  If this is a string array or an object of type Dictionary<string,string>, it will do all of the heavy conversion for you and give you a nice little string to return to the client as shown below:

   1: // Method Body
   2: Dictionary<string, string> customerInfo = new Dictionary<string, string>();
   3: customerInfo.Add("FirstName", "John");
   4: customerInfo.Add("LastName", "Doe");
   5: customerInfo.Add("EmailAddress", "JohnDoe@Domain.Com");
   6: customerInfo.Add("PhoneNumber", "555-555-1212");
   7:  
   8: return new JavaScriptSerializer().Serialize(customerInfo);

In this code snippet, I have instantiated a new Dictionary<string,string> generic object and populated with the property information for a customer.  Lastly, I instantiate a new JavaScriptSerializer object and call it's Serialize method, passing our customer information into it.  The JavaScriptSerializer will create following string from our dictionary:

   1: {"FirstName":"John","LastName":"Doe","EmailAddress":"JohnDoe@Domain.Com","PhoneNumber":"555-555-1212"}

This is just a simple string that, technically, we could have concatenated ourselves; however, you see how it converts the name-value pairs of the Dictionary object and turns them into a json Object with properties and string values.

Seems pretty simple.  Now, let's turn our customer Dictionary into a CustomerInfo object with the same four properties.  Below is the class definition:

   1: public class CustomerInfo
   2: {
   3:     public string FirstName { get; set; }
   4:     public string LastName { get; set; }
   5:     public string EmailAddress { get; set; }
   6:     public string PhoneNumber { get; set;}
   7: }

Now, if we replace our Dictionary object with our CustomerInfo object we get code that looks similar to the following, easier to read, snippet:

   1: // Method body
   2: CustomerInfo custInfo = new CustomerInfo();
   3: custInfo.FirstName = "John";
   4: custInfo.LastName = "Doe";
   5: custInfo.EmailAddress = "JohnDoe@Domain.Com";
   6: custInfo.PhoneNumber = "555-555-1212";
   7:  
   8: return new JavaScriptSerializer().Serialize(custInfo);

Sadly, running the above code will give you the following error when you attempt to run it:

CircularReferenceError

Since the JavaScriptSerializer doesn't know the definition of our CustomerInfo object, we get this Circular Reference error.  This causes us to go down one of two roads.  We can either turn the object manually back into our Dictionary object, or we can write a custom JavaScriptConverter for our CustomerInfo Class.  The nice thing about a custom converter is that once it's setup to our JavaScriptSerializer, we can then have it convert any number of CustomerInfo classes (or collections of CustomerInfo objects) we may need.

 

Writing A Custom JavaScriptConverter

Writing our own custom JavaScriptConverter is not as difficult as one may first assume.  Inside the System.Web.Script.Serialization namespace, we are provided with an abstract base class that helps us outline the definition and gets us started quickly.  In our project, let's add another class called CustomerInfoConverter.  Have the class inherit from the JavaScriptConverter class and right-click on the class name and select "Implement Abstract Class".  What you get is the following code snippet:

   1: public class CustomerInfoConverter : JavaScriptConverter
   2: {
   3:     public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
   4:     {
   5:         throw new Exception("The method or operation is not implemented.");
   6:     }
   7:  
   8:     public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
   9:     {
  10:         throw new Exception("The method or operation is not implemented.");
  11:     }
  12:  
  13:     public override IEnumerable<Type> SupportedTypes
  14:     {
  15:         get { throw new Exception("The method or operation is not implemented."); }
  16:     }
  17: }

 

The first method the JavaScriptConverter makes us define is the Deserialize method.  This method is used by the JavaScriptSerializer to convert a json object from a client into the expected type of the web method uses as a parameter.  The JavaScriptSerializer automatically converts the json object into a Dictionary<string, object> collection.  Inside this method, you could add our mappings between the Dictionary keys to a new CustomerInfo object's properties and finally return that new instance of the CustomerInfo object.  Below is the method body:

   1: CustomerInfo cust = new CustInfo();
   2: cust.FirstName = dictionary["FirstName"].ToString();
   3: cust.LastName = dictionary["LastName"].ToString();
   4: cust.EmailAddress = dictionary["EmailAddress"].ToString();
   5: cust.PhoneNumber = dictionary["PhoneNumber"].ToString();
   6:  
   7: return cust;

 

The second method the JavaScriptConverter makes us define is the Serialize method.  This method is used during the actual serialize process.  Here, we're creating a new Dictionary<string, object> collection for our CustomerInfo class.  Since our CustomerInfo class will be boxed through the method's parameter, we'll need to properly cast it before we can start adding the values to the Dictionary object.  Below is the method body:

   1: // Cast the obj parameter
   2: CustomerInfo cust = obj as CustomerInfo;
   3:  
   4: if (cust != null)
   5: {
   6:     Dictionary<string, object> result = new Dictionary<string, object>();
   7:     result.Add("FirstName", cust.FirstName);
   8:     result.Add("LastName", cust.LastName);
   9:     result.Add("EmailAddress", cust.EmailAddress);
  10:     result.Add("PhoneNumber", cust.PhoneNumber);
  11:  
  12:     return result;
  13: }
  14:  
  15: // If the obj doesn't convert for some reason, return an empty dictionary.
  16: return new Dictionary<string, object>();

 

The last item the JavaScriptConverter makes us define is the SupportedTypes property of type IEnumerable<Type>.  This property should be a collection of types that this converter supports.  Since we are using this converter only for our CustomerInfo class, the property can be simplified to the following line of code:

   1: get { return new Type[] { typeof(CustomerInfo) }; }

 

Now that we have our converter built, we can attach it to our serializer to get our json string as shown below:

   1: CustomerInfo cust = new CustomerInfo();
   2: cust.FirstName = "John";
   3: cust.LastName = "Doe";
   4: cust.EmailAddress = "JohnDoe@Domain.com";
   5: cust.PhoneNumber = "555-555-1212";
   6:  
   7: JavaScriptSerializer jss = new JavaScriptSerializer();
   8: jss.RegisterConverters(new CustomerInfoConverter[] { new CustomerInfoConverter() });
   9: return jss.Serialize(cust);

 

Talking to the Server using jQuery:

Now that we have our server-side infrastructure setup, we can begin to write our AJAX client-side code using jQuery.  Compared to all of the code we've written, this is the easier part.  Below is the JavaScript/jQuery code that can be used to call our web service:

   1: function GetCustomerFromServer()
   2: {
   3:     $.ajax({
   4:         type: "POST",
   5:         url: "/JsonService.asmx/GetCustomer",
   6:         dataType: "json",
   7:         data: "{}",
   8:         contentType: "application/json; charset=utf-8",
   9:         success: function(msg){
  10:             var custInfo = eval("(" + msg + ")");
  11:             alert(custInfo.FirstName);
  12:         }
  13:     });
  14: }

In this client-side code, we're making a HTTP Post call to our web service by calling the web method of it directly.  We are stating that we are sending and receiving data in json format.  Despite the fact that our GetCustomer() web method does not take any parameters, we still need to send an empty json object in order to have json returned. 

Lastly, we write an anonymous method to handle the successful message returned to us.  The message is evaluated in order to be turned into a json object on the client side.  We then validate the object by echoing the FirstName property in an alert box.

One thing to remind, this is for ASP.Net 2.0 Web Services.  The response from ASP.Net 3.5 Web Services IS different in that the response message (msg) has it's content in a property only called "d".  So instead of eval("(" + msg + ")"), it would be eval("(" + msg.d + ")").

Summary:

This post has covered a large amount of steps to get a json-based web service infrastructure setup using jQuery and ASP.Net 2.0 web services.  After a lot of low level research and asking questions to people, I realized that there was not a single location for this information. Hopefully, this post will help fill that gap.


kick it on DotNetKicks.comShout it

Wednesday, October 8, 2008

jQuery, AJAX, and Classic ASP

While there was a good size buzz about jQuery before Scott Guthrie announced that jQuery will be shipping with Visual Studio, I have seen even more buzz with people wanting to learn more about it.  I have seen a huge number of new posts on ways to integrate it with traditional ASP.Net WebForm projects and tips and tricks with various extensions.  I have also noticed that Dreamweaver CS4 also has it bundled with it as well.  I'm not an Adobe guy so I have no idea if it was part of CS3 or not; however, I still find the momentum of jQuery amazing right now.

 

Even though I love learning and reading about the latest and greatest that the web design/development blogs have to offer, there are times in which we don't have the luxury of rebuilding our older/legacy applications.  Because of this, and a project I recently had, I explored the prospect of using jQuery to implement/replace AJAX functionality in a classic ASP application.

 

Background

This past week at work, I was given an unusual project that involved a legacy application that we rarely touch.  You know, one of those applications that has been around for ages and it works so no one wants to touch it if they really don't have to.  This particular application in my organization was a classic ASP application that had a page which was added on a couple years back that had the beginnings of AJAX.  The project was simple, ensure the site would be IE7 and FF3 compatible, because the current JavaScript would only work in IE6.  Sounds simple right?

 

Well, in all honesty, the project was pretty simple since I just had to use the appropriate XMLHTTPRequest object; however, once I put in the examples that are scattered across the Internet, it still didn't work.  So I had a choice between rewriting the entire JavaScript, the entire page in ASP.Net (my boss suggested/approved of such actions if required), or look into using jQuery instead of a homebrew of the XHR object.  I chose the latter.

 

The AJAX logic was rather simple.  The XHR object would send a request to a different classic ASP page which interpreted the query string and send back either a string value or actual HTML code through Response.Write().  There was no true, well formatted XML and it really was just simple request/response in a stateless manner.  Since this was the first time I'd ever looked at AJAX (or AJAH as some people would call this I'm finding out), I figured that I'd post it to help others with a speedy enhancement to their classic ASP instead of doing a full rebuild (which is costly).

 

Code

Download: jQuery and Classic ASP.zip

 

Overview

The code for this post focuses on a standard HTML page that sends XHR requests to a classic ASP application.  The way the classic ASP page is used in this example is similar to many web services where it exposes a number of functionality without publishing the WSDL file.  Our XHR will be passing a query string to this page that involves an ACTION parameter and then other values for that particular action.  Based on the ACTION, the ASP page will return a string value through the Response.Write() function.  In one instance, the ASP function will produce a string that is an HTML Select element, as well to illustrate what I'm finding out is called AJAH (Asynchronous JavaScript and HTML).

 

The ASP Code

This page has no UI.  On load, the page examines the query string for the ACTION parameter and based on the value, it will called the "Hello" function or the "GetList" function.

 

The HTML Form

The basic HTML Form is simple and divided into 2 parts.  The first is your typical "Hello, World!" example where the user enters their name and the jQuery makes a request to the classic ASP page for a returning string.  The second is a simple series of cascading drop down lists that are dynamically created on by the classic ASP page and sent back.  In both examples, I use jQuery's append() function to output the response to the screen.

The jQuery AJAX call is identical in both examples with the exception to the information being passed to the ASP page and the element the response is appended to.

   1: $.ajax({
   2:     type:"POST",
   3:     url: "http://localhost/jQueryExample.asp",
   4:     dataType: "application/x-www-form-urlencoded",
   5:     data: "Action=hello&Val=" + $("#txtName").val(),
   6:     async: false,
   7:     success: function(msg){ $("#fldHello").append(msg);}
   8: })

 

Conclusion

While I have to admit I hope I don't dive too much into classic ASP in the future, I do know that this experience has made my outlook on using it a bit better from a usability stance.  jQuery has made making AJAX style calls on classic ASP very simple to implement and allows for developers to not have to write their own plumbing code.

kick it on DotNetKicks.com

Wednesday, October 24, 2007

An Introduction to PageMethods

PageMethods offer a simple way to asynchronously communicate with a server using Microsoft's ASP.Net AJAX technologies.  Unlike Update Panels which utilize the full page life cycle to synchronously update a section of the page based on the panel's triggers, PageMethods handles the transmissions manually through JavaScript.  Since everything is manual, PageMethods take a small amount of additional time to develop; however, provides an additional level of efficiency that cannot be found in UpdatePanels.

In order to begin utilizing PageMethods in your ASP.Net AJAX enabled webpage, you need to do 3 things:
  • Set the ScriptManager's "EnablePageMethods" property to "true".
  • Write Public Static WebMethods in the Code Behind file of the web page that will return the information required.
  • Write JavaScript that calls the PageMethods and reacts to the return results (information or errors).
Setting up the Script Manager
Setting up the ScriptManager to handle PageMethods is fairly straightforward; however, there's one thing that you'll need to be aware of. The "EnablePageMethods" property is only found on the ScriptManager control and not the ScriptManagerProxy control. This means that if you have a Master Page with a ScriptManager control in it used by the Content Page that will contain the PageMethods, you must set the Master Page's ScriptManger control to include the property. Doing such enables all pages that use that master page to be able to use PageMethods. I haven't investigated to see the impact this has on pages that do not utilize PageMethods.

With that out of the way, in order to enable page methods on a page, simply go to your ScriptManager control that's associated with the page and set the "EnablePageMethods" property to "True". This is "False" by default, but once you set it to "True" you're all set.
Writing the Server Code
Now that we can write PageMethods, we need to flip to our code behind files (or our <script runat=server> sections) to begin. Below are the steps that I traditionally follow when creating PageMethods:
  1. Create a new method/function that returns String or value type (Integers, Doubles, Dates, etc.).
  2. Mark the new method as Public Static (or Shared for the VB coders out there)
  3. Import the System.Web.Services namespace
  4. Add the [WebMethod()] (or <WebMethod()>) Attribute to the Function
In a nutshell, I write the method first. This allows me to get the logic in place just in case I need to modify the implementation when I'm in the middle of writing this code. Next, I ensure the proper scope and modifiers are setup on the method, and finally, mark the method as a WebMethod like I would with a web service.

[WebMethod()]
public static string MyPageMethod(string someParam)
{
return "Hello, PageMethods!";
}

When the page loads, the ScriptManager will examine the page's definition for any public static webmethods. It will then generate JavaScript code that is used to call these methods and append it into the ASP.Net AJAX's PageMethods object. This interaction allows our new method to be called from JavaScript by referencing in JavaScript the method of the same name of the PageMethods object ( PageMethods.MyPageMethod(...) in this case - more on this below).

Making the calls with JavaScript

So far we have enabled PageMethods in our ScriptManager and have made some static web methods in our code that the ScriptManager will create JavaScript from. Now, we need to write our own JavaScript code to call these PageMethods so that we can retrieve the information they provide.

In order to create the JavaScript code to call the PageMethods, you simply have to make have 3 things:

  1. The script that calls the PageMethod itself
  2. A function to call on each successful PageMethod call
  3. A function to call when ever there was an error in the PageMethod call.

The code to call the PageMethod is very simple.

function callerMethod() {
PageMethods.MyPageMethod(PARAM1,
PARAM2,
PARAMn,
callerMethod_Success,
callerMethod_Failure);
}

Let's pick this apart now. What we have above is a simple JavaScript function called "callerMethod". Inside of this function, we do a simple call to our method's JavaScript counterpart that got attached to the PageMethods JavaScript object that's included by the ScriptManager. Next, we pass any parameters required (signified by the "PARAM1", "PARAM2", and "PARAMn" in this case) followed by a pointer/name of the method to call on a successful transmission and the same for the failed transmission.

One optional parameter that I've purposefully excluded can appear at the very end is designated for the current UserContext. In a later posting, I will provide more information revolving around this parameter and how to use it to provide more powerful communication models in the code. For right now, we will exclude the parameter because we can.

Now that we've created the call, we need to create the functions that'll be called when the transmission succeeds or fails.

function callMethod_Success(results, userContext, methodName) {
alert(results);
}

function callMethod_Failure(errors, userContext, methodName) {
alert(errors.get_Message());
}

Again, these are very simple methods that do nothing other than alerting the user of the resulting value on success or the error messsage on failure. The names of the methods are completely up to you naturally; however, whatever you name the methods, each require 3 parameters.

The first parameter of is for the returned value or error that the call to the PageMethod created. On success, you can immediately begin using the results as if they were a normal value passed into any regular function (since it's either a String or Number JavaScript type). On failure, the PageMethod and ScriptManage return an object with information about the error. The get_Message() method of the error object will return the error message that was thrown during the transmission. Once you have the resulting value (success or failure), you can then write JavaScript logic to do whatever you want with it.

The second parameter is the userContext. Like previously stated, this will be covered in a future posting. For now, just remember that you can send the userContext to the PageMethod and read the resulting context on the returning trip.

The last parameter of the success and failure methods is the methodName. This parameter provides a simple string that represents the PageMethod name that was called and caused the success or failure function to be triggered. This is a great addition to the returning functions since it allows you to reuse success and/or failure functions across different PageMethods and produce custom logic based on which PageMethod was called. While I can see some merit in having a single success and a single failure (or even 1 function that does both), I wouldnt' want to maintain the if statements that would spawn from such logic.

Summary

While the example code here isn't too useful (like most Hello, World projects), it hopefully will get you started on using PageMethods. In future postings, I'll provide some addition information and techniques that you can use in order to utilize PageMethods by using the UserContext object, identifying more in depth the data types that can and cannot be passed, and also object serialization through JSON. In addition to these items, I'll also be keeping an eye out on any items that may be not quite so obvious when you're work with PageMethods. Down the road a bit, I'll do a very similar post to this about using local WebServices instead of PageMethods and why.