Calling WebServices via AJAX Part 1

Several months ago, one of my customers asked me a fairly pointed question: 

How do you call web services from JavaScript? 

At least, that's what they asked me, but not really what they meant (I'll explain what their real question was in a subsequent post).

 

Consuming Server-Side Data Using JavaScript

In the old days, you probably wrote a web service that looks like this.

 

using System;


using System.Web.Services;


 


[WebService(Namespace = "http://tempuri.org/")]


public class Service : System.Web.Services.WebService


{


    [WebMethod]


    public string HelloWorld() 


    {


        return "Hello World";


    }    


}





About 6 years ago, you might have written JavaScript code to call that web service directly using the XMLHTTPRequest ActiveX object.  Let's take a look at the way things used to be, referencing my earlier post on a nostalgic look at using XMLHTTPRequest directly from JavaScript to call web services.






var xmlhttp; 


 


function on_click() 


{


var xmlToSend = "<?xml version='1.0' encoding='utf-8'?>"; 


xmlToSend += "<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' "; 


xmlToSend += "xmlns:xsd='http://www.w3.org/2001/XMLSchema' "; 


xmlToSend += "xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>"; 


xmlToSend += "<soap:Body><HelloWorld xmlns='http://tempuri.org/' />"; 


xmlToSend += "</soap:Body></soap:Envelope>"; 


 


var xmldoc = new ActiveXObject("Microsoft.XMLDOM"); 


xmldoc.loadXML(xmlToSend); 


xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); 


xmlhttp.onreadystatechange = state_Change; 


xmlhttp.open("POST", "http://intranet.litwareinc.com/xdomain/WebService.asmx", false); 


xmlhttp.setRequestHeader ("SOAPAction", "http://tempuri.org/HelloWorld"); 


xmlhttp.setRequestHeader ("Content-Type", "text/xml"); 


xmlhttp.send(xmldoc); 


 


alert(xmlhttp.responseXML.xml);


}


 


function state_Change() 


{


    // if xmlhttp shows "loaded"


    if (xmlhttp.readyState==4)


    { 


        // if "OK"


        if (xmlhttp.status==200)


        { 


            alert("OK");


        }


        else 


        { 


            alert("Problem retrieving XML data"); 


        } 


    }


}





Wow, no wonder this customer had a hesitation to calling SOAP web services from JavaScript... that's some ugly code, and I sure don't want to have to write that ugliness each time I call a web service. 



It goes without saying that this was revolutionary just 6 or 7 years ago, but we've learned enough since then to know that you just don't really want to do this anymore.  First, there's not a full SOAP framework in JavaScript, meaning that the serialization/deserialization is going to be left up to you as is the SOAP fault handling.  There might be a library somewhere that provides these capabilities, but honestly ask yourself if this is the best approach.  How much perf overhead are you wasting with all that string parsing into a DOM representation?  Just because you can, doesn't mean you should. 



Some of you are probably thinking, "whaddya mean, the way things used to be?  I just coded that exact code this week?!?" 



Enabling ASMX With JSON



Instead of focusing on XML and SOAP in JavaScript, the world has come to realize that JavaScript Object Notation, or JSON, is much more appropriate for these scenarios.  My elaborate XML above becomes something much simpler, the HTTP body of the request simply consisting of:



{"d":"Hello World"}



ASP.NET 3.5 includes AJAX capabilities that make JavaScript programming much, much easier to work with by enabling ASMX to be able to speak both SOAP/XML as well as JSON.  To do this, we'll make one tiny change to the service.






using System;


using System.Web.Services;


using System.Web.Script.Services;


 


[WebService(Namespace = "http://tempuri.org/")]


[ScriptService]


public class Service : System.Web.Services.WebService


{


    [WebMethod]


    public string HelloWorld() 


    {


        return "Hello World";


    }    


}





Notice the addition of the ScriptService attribute on our service.  This is an attribute new to ASP.NET 3.5, found in the System.Web.Script.Services namespace.  It enables an ASMX service to be exposed using either SOAP or JSON over HTTP.  Adding this attribute enables ASMX to automatically emit a JavaScript proxy type just by appending "/js" to the querystring.  For instance, if my service is in a file called "service.asmx", I would enter "service.asmx/js" into the browser and would be prompted to save something.  Save to a file and open that file in Notepad to take a peak at what was generated.






var Service=function() {


Service.initializeBase(this);


this._timeout = 0;


this._userContext = null;


this._succeeded = null;


this._failed = null;


}


Service.prototype={


_get_path:function() {


 var p = this.get_path();


 if (p) return p;


 else return Service._staticInstance.get_path();},


HelloWorld:function(succeededCallback, failedCallback, userContext) {


return this._invoke(this._get_path(), 'HelloWorld',false,{},succeededCallback,failedCallback,userContext); }}


Service.registerClass('Service',Sys.Net.WebServiceProxy);


Service._staticInstance = new Service();


Service.set_path = function(value) { Service._staticInstance.set_path(value); }


Service.get_path = function() { return Service._staticInstance.get_path(); }


Service.set_timeout = function(value) { Service._staticInstance.set_timeout(value); }


Service.get_timeout = function() { return Service._staticInstance.get_timeout(); }


Service.set_defaultUserContext = function(value) { Service._staticInstance.set_defaultUserContext(value); }


Service.get_defaultUserContext = function() { return Service._staticInstance.get_defaultUserContext(); }


Service.set_defaultSucceededCallback = function(value) { Service._staticInstance.set_defaultSucceededCallback(value); }


Service.get_defaultSucceededCallback = function() { return Service._staticInstance.get_defaultSucceededCallback(); }


Service.set_defaultFailedCallback = function(value) { Service._staticInstance.set_defaultFailedCallback(value); }


Service.get_defaultFailedCallback = function() { return Service._staticInstance.get_defaultFailedCallback(); }


Service.set_path("/WebSite7/Service.asmx");


Service.HelloWorld= function(onSuccess,onFailed,userContext) {Service._staticInstance.Hell





This might look like gibberish a bit, but this is actually some really, really useful stuff that makes calling web services from JavaScript simple.  This code represents a JavaScript proxy that makes it simple to call your service using ASP.NET 3.5.  Notice the line:






Service.registerClass('Service',Sys.Net.WebServiceProxy);





This enables our JavaScript class to derive from WebServiceProxy and add new capabilities to it, such as a new method called HelloWorld (the last line in generated proxy).  If you are using ASP.NET 3.5, you should be blissfully ignorant that this stuff exists, since the ScriptManager control will automagically handle all of this stuff for you (more on that later).  However, if you are using something that isn't ASP.NET, for instance ColdFusion or PHP, you can still use ASP.NET AJAX Library.



I copied the generated proxy above into a JavaScript class called "proxy.js". Since I can leverage ASP.NET 3.5, look at how simple this becomes... here's the client-side code in its entirety.






<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


<html xmlns="http://www.w3.org/1999/xhtml">


<head>


    <title>Calling a Service</title>


    <script src="MicrosoftAjax.js"type="text/javascript" language="javascript"></script>
   1:  
   2:     <script src="proxy.js" type="text/javascript" language="javascript">
   1: </script>
   2:     <script language="javascript" type="text/javascript">
   3:         
   4:         function onClick()
   5:         {
   6:             var proxy = Service.HelloWorld(onSuccess, onFailure);
   7:         }
   8:  
   9:         function onSuccess(sender, e)
  10:         {
  11:             alert(sender);
  12:         }
  13:         
  14:         function onFailure(sender, e)
  15:         {
  16:             alert("Problem retrieving XML data");
  17:         }
  18:         
  19:     
</script>


</head>


<body>    


    <div>


        <input type="button" onclick="onClick();return false;" />


    </div>


</body>


</html>





Look at that... 3 lines of client-side JavaScript code.  Notice there are no runat="server" tags at all, it's just a pure HTML page with some JavaScript, there's absolutely no server-side anything to this.  The astutue reader might notice the script references and think, "Oh yeah?  Where'd the MicrosoftAjax.js script stuff come from?"  That's what ships with ASP.NET 3.5, but you can download it and use the client side JavaScript library separately without using ASP.NET at all.  You can find out more from the Installing ASP.NET AJAX Documentation:





  1. Download the Microsoft AJAX Library 3.5 from the ASP.NET AJAX Downloads Web site.





  2. Unzip the MicrosoftAJAXLibrary.zip package.





  3. Copy the JavaScript files to the Web site.





It makes total sense, since it's really just a bunch of JavaScript.  That means that you can use the ASP.NET AJAX capabilities from JSP, ColdFusion, or even PHP.  Some folks even created a CodePlex project to make it even easier to use the ASP.NET AJAX libraries from PHP.



Using the ScriptManager Control



One of the things that ASP.NET makes simpler for you is the ability to skip the step above where you copy the proxy code (recall that I copied into a file called proxy.js)... ASP.NET AJAX will handle the proxy stuff for you if you use a very useful control called ScriptManager.  Further, you don't need to pull in the script reference for MicrosoftAjax.js, because the ScriptManager will do that for you, as well.  Using ASP.NET, the code simply becomes:






<%@ Page Language="C#"%>


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


<html xmlns="http://www.w3.org/1999/xhtml">


<head runat="server">


    <title>Calling a Service</title>


    <script language="javascript" type="text/javascript">
   1:  
   2:         
   3:         function onClick()
   4:         {
   5:             var proxy = Service.HelloWorld(onSuccess, onFailure);
   6:         }
   7:  
   8:         function onSuccess(sender, e)
   9:         {
  10:             alert(sender);
  11:         }
  12:         
  13:         function onFailure(sender, e)
  14:         {
  15:             alert("Problem retrieving XML data");
  16:         }
  17:         
  18:     
</script>


</head>


<body>


    <form id="form1" runat="server">


        <asp:ScriptManager ID="ScriptManager1" runat="server" >


            <Services>


                <asp:ServiceReference Path="Service.asmx" />


            </Services>


        </asp:ScriptManager>


    <div>    


        <input type="button" onclick="onClick();return false;" />    


    </div>


    </form>


</body>


</html>





Next up... replacing our ASMX with WCF.



more : blogs.msdn.com




Technorati Tags: ,



Related Posts by Categories



Widget by Hoctro

Enter your email address:

Delivered by FeedBurner

Followers



Source Code

Tips