Accessing Web Services From DHTML

David Massy
Microsoft Corporation

January 22, 2001

Contents

What Is a Web Service and Why Should I Care?
So How Do I Use a Web Service From DHTML?
It's All About Services

One of the most exciting and interesting developments taking place on the Internet at the moment is the emergence of Web Services. For this column, I'll discuss how you can access these Web Services from DHTML by using the new WebService behavior.

What Is a Web Service and Why Should I Care?

Typically when we think of the Internet, we think of a browser displaying HTML pages that are retrieved from a server. However the connectivity of the Internet offers much greater potential if computers connected to the Internet can share information without the need for someone to use a browser. Web Services expose functionality as a service that another computer can call. Examples of such services include supplying stock quotes, weather information, current inventory levels, flight and traffic information, etc. The emerging standard for Web Services is the Simple Object Access Protocol (SOAP), which uses XML as the interchange format, allowing computers to exchange information regardless of operating system, database, or other software. I tend to think of accessing a Web Service as being much like calling a function in a programming language and receiving a response—except the function resides on another machine.

Web Services are at the heart of Microsoft's .NET initiative, and Microsoft is providing tools to assist in exposing and consuming Web Services. For more information and an in-depth discussion of Web Services, take a look at http://msdn.microsoft.com/webservices/.

So why would you wish to access a Web Service from DHTML? Let's imagine a Web page for an airline where customers can find out whether flights will arrive on time. Many airlines offer such Web pages, and they typically have fields that allow users to enter flight numbers and a submit button to submit the flight numbers to a server so fresh pages can be returned to users with the requested information. This model is a little inefficient and clunky in the way it works. In the ideal world, we don't really wish to create a new Web page; we just want to update a single area of the page for the results. This way the user is not taken to a new page and only the requested data need be communicated back to the browser.

Now let's imagine that the airline exposes a Web Service for current flight information, allowing information about flight delays to be shared easily with other computers. Using this model, we can now have an HTML page that uses the Web Service to retrieve the required flight information. When the user enters a flight number and clicks submit, a simple call to the Web Service on the server is made and the flight information is retrieved without having to generate and send a complete HTML page to the browser. This is a simple example of the use of a Web Service from a browser. But take this a step further and think of how we might utilize Web Services in a corporate environment to get updates on inventory and pricing to build a powerful sales application with great interaction, all the while avoiding the pitfalls associated with Web page navigation.

So How Do I Use a Web Service From DHTML?

A Web Service can be accessed from the browser with script using XML over http in a very easy process. The WebService behavior is an attached behavior, so it will work with Internet Explorer 5.0 or later. The WebService behavior is written as an HTML Component or HTC and uses the emerging SOAP standard to communicate with Web Services on the server.

For the purposes of demonstration, we'll use the math test Web Service exposed on a public server. This Web Service is extremely simple and offers methods to Add, Subtract, Multiply, and Divide integers. You can try these methods directly from the .asmx page that exposes the Web Service. Simply enter two integers for the Add method and click on the link to invoke the method. A new browser window then opens to show the result.

See the .asmx page in action.

Now let's take a look at a DHTML page that uses the Add method:

<html>
<head>
<script language="JavaScript">
var iCallID;

function init()
{
   service.use("mathservice.asmx?SDL","math");
}

function onmyresult()
{
    if((event.result.error)&&(iCallID==event.result.id))
   {
      var xfaultcode = event.result.errorDetail.code;
      var xfaultstring = event.result.errorDetail.string;
      var xfaultsoap = event.result.errorDetail.raw;

      // Add code to output error information here
      alert("Error");
   }
   else
   {
      service.innerHTML= "The method returned the result : " + event.result.value;
   }
}
</script>
</head>
<body onload="init();">
<BR>
Enter first Value <input type='text' id='ip1'>
<BR>
Enter Second value <input type='text' id='ip2'>
<BR>
<button onclick='iCallID = service.math.call("Add",ip1.value,ip2.value);'>Call Add Web Method</button>
<div id="service" style="behavior:url(webservice.htc)" onresult="onmyresult();">
</div>

</body>
</html>

See the page in action calling the Add method.

First take a look at the div element with the id of service toward the end of the HTML file. This div has the webservice.htc behavior attached to it. This behavior takes care of all the effort involved in calling the service so we don't have to worry about it. The onresult event handler is called by the behavior when the method being called returns a result. Next look at the init() script function that is executed when the page loads. This sets up the service we wish to call with the friendly name math. The Web Service itself is exposed through the mathservice.asmx file.

The button element in the file executes the call to the Add method that is exposed by the Web Service, offering the values of the two text boxes as parameters. The onmyresult() script function then handles the result and displays it on the page.

Note that the method calls are asynchronous. This means that once the call to the method on the server has been made, the script on the page will continue to run and the returned result from the method will come later as a callback function. The alternative to asynchronous calls is a synchronous call that returns the result directly to the call to the function. This synchronous calling is the traditional technique used when calling functions in script. You call a function and the function executes, then returns the result. However, such synchronous calling is inappropriate for use on the Internet because the Web Service is executing on a different machine than the calling script on the client machine. The reality is that the Web Service might not be available; to have the processing of the browser stall while waiting for the Web Service to return would create an unacceptable user experience.

There are some restrictions on using the WebService behavior that should be explained. The WebService behavior is an .htc file built from HTML and script, and is therefore as secure as running script in an HTML page. It's not subject to the ActiveX control level of security associated with running binary native code. To ensure this level of security, both the .htc file and the Web Service must be on the same Internet domain as the HTML file that uses them. This restriction protects users from nefarious use of the service and the data by other domains. So the best way to experiment with this functionality is to host a Web Service with the WebService behavior and the HTML page together. The easiest way to do this is by downloading the .NET Framework beta.

Despite the necessary restriction, Web Services have the potential to be extremely useful for communicating with a server and avoiding navigation of pages in the process. Let's look at another example at http://dotnet.microsoft.com/. In this example, the HTML page is using a service that offers a search facility. The results of the search are then used to populate an area of the page. To see one of the reasons why this offers a more attractive user experience, scroll to the bottom of the page. If this search used a traditional form submission to generate a new page with results from the server, when you clicked the search button a new page would be returned and you would find yourself back at the top of the page. However, in this example, using a Web Service and DHTML to populate part of the page with fresh results, you have the results returned without the entire page being changed. The experience is, as a result, more immersive and much less jarring.

Let's take a closer look at how the data is returned and accessed. In the following snippet of code, we see that the search method is called using the search query string entered from the text field.

         // ---------------------------------------------------------------------------
         // -- Called when the user clicks the button to invoke a search
         // ---------------------------------------------------------------------------
         function CallBestBets(elm) {
            var sQuery = elm.value;
            bbLookupResultText.style.display = "none";
            bbLookupResultDisplay.style.display = "none";
            if (sQuery != "") {
               iCallID = service.BestBets.call("GetBestBets", CallBackBestBets, sQuery);
            } else {
               alert("Please enter a value to Lookup in the BestBets WebService!");
            }
         }         // --------------------------------------------------------------
         // -- Called when the user clicks the button to invoke a search
         // --------------------------------------------------------------
         function CallBestBets(elm) {
            var sQuery = elm.value;
            bbLookupResultText.style.display = "none";
            bbLookupResultDisplay.style.display = "none";
            if (sQuery != "") {
               iCallID = service.BestBets.call("GetBestBets", CallBackBestBets, sQuery);
            } else {
               alert("Please enter a value to Lookup in the BestBets WebService!");
            }
         }

In the next snippet we see how the results are returned and used to populate the DHTML page.

         // -----------------------------------------------------------
         // -- Callback to display XML->XSL->HTML results
         // -----------------------------------------------------------
         function CallBackBestBets(result) {
             if(result.error)  {
                 var xfaultcode   = result.errorDetail.code;
                 var xfaultstring = result.errorDetail.string;
                 var xfaultsoap   = result.errorDetail.raw;
                 alert("Errors xfaultcode->" + xfaultcode + " xfaultstring->" + xfaultstring + " xfaultsoap->" + xfaultsoap);
             } else  {
               // -- load the raw SOAP call XML and ALL from the Service
               var xmlResult = result.raw.xml;
               if (xmlResult != "" && xmlResult != null) {
                  bbXML.loadXML(xmlResult);
                  // -- get count and display
                  var bbCount = bbXML.selectSingleNode("//BestBetsCount").text;
                  if (bbCount != "0") {
                     var sResult = "<hr color='#cccccc' size='1'><font color='navy' face='verdana, arial, helvetica' size='2'>";
                     sResult += "We found a total of <b>";
                     sResult += bbCount;
                     sResult += "</b> results from the BestBets WebService.</font>";
                     bbLookupResultText.innerHTML = sResult;
                  } else {
                     var sResult = "<hr color='#cccccc' size='1'><font color='navy' face='verdana, arial, helvetica' size='2'>";
                     sResult += "The Search WebService returned zero results for the phrase you entered...</font>";
                     bbLookupResultText.innerHTML = sResult;
                  }
                  bbLookupResultText.style.display = "";
                  // -- transform XML to HTML using XSL
                  var sXML = bbXML.transformNode(bbXSL.XMLDocument);
                  if (bbXML.parseError.reason == "") {
                     bbLookupResultDisplay.innerHTML = sXML;
                     bbLookupResultDisplay.style.display = "";
                  } else {
                     bbLookupResultDisplay.innerHTML = bbXML.parseError.reason;
                     bbLookupResultDisplay.style.display = "";
                  }
               }
             }
         }
         // ---------------------------------------------------------------------------
         // -- Callback to display XML->XSL->HTML results from WebService
         // ---------------------------------------------------------------------------
         function CallBackBestBets(result) {
             if(result.error)  {
                 var xfaultcode   = result.errorDetail.code;
                 var xfaultstring = result.errorDetail.string;
                 var xfaultsoap   = result.errorDetail.raw;
                 alert("Errors xfaultcode->" + xfaultcode + " xfaultstring->" + xfaultstring + " xfaultsoap->" + xfaultsoap);
             } else  {
               // -- load the raw SOAP call XML and ALL from the Service
               var xmlResult = result.raw.xml;
               if (xmlResult != "" && xmlResult != null) {
                  bbXML.loadXML(xmlResult);
                  // -- get count and display
                  var bbCount = bbXML.selectSingleNode("//BestBetsCount").text;
                  if (bbCount != "0") {
                     var sResult = "<hr color='#cccccc' size='1'><font color='navy' face='verdana, arial, helvetica' size='2'>";
                     sResult += "We found a total of <b>";
                     sResult += bbCount;
                     sResult += "</b> results from the BestBets WebService.</font>";
                     bbLookupResultText.innerHTML = sResult;
                  } else {
                     var sResult = "<hr color='#cccccc' size='1'><font color='navy' face='verdana, arial, helvetica' size='2'>";
                     sResult += "The Search WebService returned zero results for the phrase you entered...</font>";
                     bbLookupResultText.innerHTML = sResult;
                  }
                  bbLookupResultText.style.display = "";
                  // -- transform XML to HTML using XSL
                  var sXML = bbXML.transformNode(bbXSL.XMLDocument);
                  if (bbXML.parseError.reason == "") {
                     bbLookupResultDisplay.innerHTML = sXML;
                     bbLookupResultDisplay.style.display = "";
                  } else {
                     bbLookupResultDisplay.innerHTML = bbXML.parseError.reason;
                     bbLookupResultDisplay.style.display = "";
                  }
               }
             }
         }

Here we can see that the results are returned as XML, which is then loaded into an XML document using:

 bbXML.loadXML(xmlResult);

and performing a transform to the loaded XML document: var sXML = bbXML.transformNode(bbXSL.XMLDocument);


This example shows how using XML as the basis for data exchange over the Internet is powerful, allowing Internet Explorer's XML support to easily read data and manipulate it to form a display.

It's All About Services

In this column I've barely started to uncover the potential that this technology offers, and I expect to write further on this topic as the Web Service model evolves and DHTML support for accessing services also develops.

David Massy works on Internet Explorer as a program manager.

posted @ 2004-08-06 18:56  Martin XJ  阅读(467)  评论(0编辑  收藏  举报