Call a Web service from Javascript utilizing SOAP without any extra libraries.

Environment: ASP .NET C#, and VB

What is all the buzz about AJAX?  It's all about calling a web service from your client web site without posting your web page to the host.  Well AJAX is more than that but in this example we'll consume a web service using a Javascript client.  What is AJAX? Well it's really a group of interrelated techniques used to send and receive data asynchronously from the host, keeping your browser responsive while gathering small amounts of data without refreshing the entire web page.   You call the web service using the XMLHttpRequest object built into your browser's DOM. Most newer browsers support the DOM object so the XMLHttpRequest object will be available. Calling a web service asynchronously from Javascript can significantly reduce the round trip time required to get a small amount of data from the host in using background processing.

If you are familiar with .NET web services you understand how efficient these calls to the host are, no html web page, just the data you want returned in (often) XML format. ASP .NET allows you to build powerful data driven applications...but all that reposting to the host costs a lot of bandwidth.  The XMLHttpRequest object has been around since 2000 and is found on virtually any browser in use today. In this example we will call an ASP .NET web service written in C# using a SOAP (Simple Object Access Protocol) envelope from a Javascript client. Download the Javascript code (.zip file) for this example.

The code that allows you to call a web service from javascript:

First the Javacript code to encapsulate the XMLHttpRequest object. This is a Javascript object with which we will create a new instance and and make the asynchronous call to the host.

function WebSvc()   // Class Signature
{
    
    // Encapsulates the elements of a XMLHttpRequest to an ASP .Net Web Service
    
    
    WebSvc.prototype.CallWebService = function(url, soapXml, callback)
    {
        
        // Calls web service with XMLHttpRequest object. Utilizes a SOAP envelope for
        // transport to and from Server. This is an asynchronous call.
        // PARAM url - Fully qualified url to web service .asmx file
        // PARAM sopXml - String containing SOAP envelope with request
        // PARAM callback - Callback with signature callback(result,data) when call returns.
        

        var xmlDoc = null;

        if (window.XMLHttpRequest)
        {
            xmlDoc = new XMLHttpRequest(); //Newer browsers
        }
        else if (window.ActiveXObject) //IE 5, 6
        {
            xmlDoc = new ActiveXObject("Microsoft.XMLHTTP");
        }

        if (xmlDoc)
        {
            //callback for readystate when it returns
            var self = this;
            xmlDoc.onreadystatechange = function() { self.StateChange(xmlDoc, callback); };

            //set up the soap xml web service call
            xmlDoc.open("POST", url, true);
            xmlDoc.setRequestHeader("Content-Type", "text/xml");
            xmlDoc.setRequestHeader("Content-Length", soapXml.length);
            xmlDoc.send(soapXml);
        }
        else
        {
            if (callback)
            {
                callback(false, "unable to create XMLHttpRequest object");
            }
        }
    };

    WebSvc.prototype.StateChange = function(xmlDoc, callback)
    {
        // Callback supplied for XMLHttpRequest Object to monitor state and retrieve data.
        // PARAM xmlDoc - XMLHttpRequest Object we're watching
        // PARAM callback - Callback function for returning data, signature CallBack(result,data)
        
        if (xmlDoc.readyState === 4)
        {
            var text = "";

            if (xmlDoc.status === 200)
            {
                text = xmlDoc.responseText;
            }

            // Perform callback with data if callback function signature was provided, 
            if (callback !== null)
            {
                callback(xmlDoc.status === 200, text);
            }
        }
    };
}
    


Next create a simple web service that we will be calling.  In this case it's a simple method that takes a string as input and returns an XML document with a <date_time> element containing the string we passed as input and the date and time.  Obviously you would want to return a status element that allowed you to determine whether the call was successful, but in this case there's not much that can go wrong.  An easy way to return an XML document from a web service is to return an XmlNode which is easily obtained from the XmlDocument.DocumentElement;

This is the C# ASP .NET web service we will call with from our web page Javascript. The method requires a string as input.
    [WebMethod]
    public XmlNode GetTime(string input) 
    {
        XmlDocument doc = new XmlDocument();
        doc.LoadXml("<envelope><date_time>" + input + "  " + DateTime.Now.ToLongDateString() + 
                    DateTime.Now.ToLongTimeString() + "</date_time></envelope>");
        
        return doc.DocumentElement;
    }                
    


When we call the web service we will be using a SOAP envelope to communicate our request.  This simple javascript function creates the SOAP envelope and includes the input parameter that the C# Web Service will be expecting.  This code is displayed here as an image but you can view the source of this page and copy the actual javascript function.

    


We supply a callback function so we can execute the request asyncronously from our web page.  This allows the web page to continue to be responsive while the client to host request is made.  When the call to the host completes the callback function will display the result.  If we don't want the callback function we supply a null as a second parameter to the call. In the following case a string parameter is passed into the web service. In this example we are using the XMLHttpRequest.responseText value and will receive the result in the form of plain text, and therefore will parse out the <date_time> tag ourselves to get the result returned by the server. The XMLHttpRequest object can also return an XML document but that is beyond the scope of this article.
    
    // This is the callback function that displays the result of the web service call
    function callComplete(result, data)
    {
        if (result)
        {
            alert("SUCCESS " + getTagValue(data, "date_time"));
        }
        else
        {
            alert("Error occurred calling web service.");
        }
    }
    
// This function is called with a text input that is formatted with the server date and time // when the web service call returns. function getTime(message) { var soap = createSoapHeader(message);
//create the web service object and make the call var webServiceCall = new WebSvc(); webServiceCall.CallWebService("services/WebService.asmx", soap, callComplete); }
// Simple Javascript function that returns data contained within a set of tags. function getTagValue(inputStr, tagName) { // Simple function to search for tagged element in a string, // this function will not recurse and simply finds first ocurrence // of tag in document. // PARAM inputStr - string containing tagged document // PARAM tagName - name of element to locate // RETURNS string data between tagName element or "" if not found var stag = "<" + tagName + ">"; var etag = "<" + "/" + tagName + ">"; var startPos = inputStr.indexOf(stag, 0); if (startPos >= 0) { var endPos = inputStr.indexOf(etag, startPos); if (endPos > startPos) { startPos = startPos + stag.length; return inputStr.substring(startPos, endPos); } } return ""; }

Call the web service from your web page as follows, supplying a parameter to the web service.
   
onclick="getTime(getElementById('echoText').value)
    


To test the web service call, click the button. Download the Javascript code (.zip file) for this example.

Important: If the address in the browser doesn't include www.hendricksongroup.com this call won't work, for instance if your browser address bar shows http://hendricksongroup.com or any other URL the XMLHttpRequest object won't let you make cross site calls (more information below).

  

Using this technique will provide your site with increased responsiveness and dramatically reduce the bandwidth requirements when only a portion of the web page data is required. One important consideration is that the XMLHttpRequest object will not allow cross site calls. For instance the call won't work if you landed on the site as http://yoursite.com and the call is made to http://www.yoursite.com.