Call SOAP Web services with Ajax, Part 1: Build the Web services client![]() | ![]() |
![]() |
Level: Intermediate James Snell (jasnell@us.ibm.com), Software Engineer, IBM 11 Oct 2005 Implement a Web browser-based SOAP Web services client using the Asynchronous JavaScript and XML (Ajax) design pattern. This paper is the first of a short series that illustrates the implementation of a cross-platform, JavaScript-based SOAP Web services client based on the Asynchronous JavaScript and XML (Ajax) design pattern for Web applications. Popularized through its use in a number of well-known Web application services like GMail, Google Maps, Flickr, and Odeo.com, Ajax provides Web developers with a way of expanding the value and function of their Web applications by using asynchronous XML messaging. The Web Services JavaScript Library introduced here expands on the fundamental mechanisms that power the Ajax pattern by introducing support for invoking SOAP-based Web services. Invoking SOAP Web services from within a Web browser can be a tricky exercise, particularly because the most popular Web browsers each handle generating and processing of XML in slightly different ways. There are few standard APIs or capabilities for XML processing that all browsers implement consistently. One of the mechanisms that browser implementers agree on is the XMLHttpRequest API, which is at the heart of the Ajax design pattern. Thoroughly described in another recently published paper on developerWorks written by Philip McCarthy, XMLHttpRequest is a Javascript object that you can use to perform asynchronous HTTP requests. The paper describes a sequence diagam (see Figure 1) that is very help in understanding how the XMLHttpRequest object enables the Ajax design (see Resources for a link to the full paper). Figure 1. Philip McCarthy's Ajax Roundtrip sequence diagram ![]() From this diagram you can see exactly how the XMLHttpRequest object functions. Some piece of JavaScript running within the Web browser creates an instance of the XMLHttpRequest and a function that serves as an asynchronous callback. The script then uses the XMLHttpRequest object to perform an HTTP operation against a server. When a response is received, the callback function is invoked. Within the callback function, the returned data can be processed. If the data happens to be XML, the XMLHttpRequest object will automatically parse that data using the browser's built in XML processing mechanisms. Unfortunately, it's in the details of how the XMLHttpRequest object automatically parses the XML where the primary difficultly with the Ajax approach comes into play. For instance, suppose that the data that I am requesting is a SOAP envelope that contains elements from a number of different XML Namespaces, and I want to grab the value of the Listing 1. A SOAP Envelope with multiple namespaces
In the Mozilla and Firefox browsers, extracting the value of the Listing 2. The method for retrieving the attr attribute in Mozilla and Firefox does not work in Internet Explorer
Unfortunately, this code will not work in Internet Explorer Version 6 because the browser does not implement the getElementsByTagNameNS function and, in fact, takes the rather unhelpful approach of treating XML Namespace prefixes as if they were part of the element and attribute names. Internet Explorer's lack of great support for XML Namespaces makes it rather difficult to deal with namespace-intensive XML formats like SOAP in a browser-independent manner. To perform something as simple as grabbing the value of an attribute in the result, you have to write special case code such that the expected behavior is consistent across multiple browsers. Luckily, this special case code can be encapsulated and reused. In order to invoke Web services from within a Web browser and reliably process the SOAP messages, you need to first understand the security issues. (See the sidebar "A Word about Security.") You also need to write a JavaScript script library (Figure 2) that can abstract away the inconsistencies of the underlying browser XML implementations, allowing you to work directly with the Web services data. Figure 2. Invoking Web Services from Javascript within the Web Browser using the Web Services JavaScript Library ![]() The Web Services JavaScript Library (ws.js) illustrated in Figure 2 is a collection of JavaScript objects and utility functions that provide a basic level of support for SOAP 1.1-based Web Services. Ws.js defines the following objects:
At the core of ws.js is the WS.Call object which provides the methods for invoking a Web service. WS.Call is primarily responsible for the interactions with the XMLHttpRequest object and the processing of SOAP responses. The WS.Call object exposes the following three methods:
While the WS.Call object is generally not much more than a thin wrapper on top of the XMLHttpRequest object, it does perform a number of actions that will make your life easier. These actions include setting the SOAPAction HTTP header that is required by the SOAP 1.1 specification.
The API presented by the Web services JavaScript Library is rather straightforward. The SOAP.* objects ( Listing 3. Building a SOAP Envelope
Listing 4 shows the SOAP Envelope that is produced by the code in Listing 3. Listing 4. Building a SOAP Envelope
If the SOAP Envelope that you are creating is representative of an RPC-Style request, the SOAP.Body element provides a Listing 5. Building an RPC-Request Envelope
Each parameter is passed in as a structure of JavaScript objects with the following expected properties:
For example, to specify a parameter named "abc" with an XML Namespace of "urn:foo", an xsi:type of "int" and a value of "3," I would use the code: Once I have built the SOAP.Envelope for the service request, I would pass that SOAP.Envelope off to the WS.Call objects As an alternative to building the SOAP.Envelope manually, I could pass the operation WS.QName, the parameters array, and the encoding style to the WS.Call object's Listing 6. Using the WS.Call object to invoke a Web service
Upon calling either the To make it possible to extend the pre- and post-processing of the SOAP messages, the WS.Call object allows you to register a collection of WS.Handler objects, as shown in Listing 7. These are invoked for every request, every response, and every error during the invocation cycle. New handlers can be implemented by extending the WS.Handler JavaScript object. Listing 7. Creating and registering response/response handlers
Handlers are most useful for the task of inserting or extracting information from the SOAP Envelopes being passed around. For instance, you could imagine a handler that automatically inserts appropriate Web Services Addressing elements into the header of the SOAP Envelope as in the example shown in Listing 8. Listing 8. A sample handler that adds a WS-Addressing Action header to the request
WS.Binder objects (Listing 9) perform custom serialization and deserialization of SOAP.Element objects. WS.Binder implementations must provide the following two methods:
Listing 9. A sample WS.Binding implementation
I have provided a sample project to illustrate the basic functionality of the Web Services JavaScript Library. The Web service (shown in Listing 10) used by the demo has been implemented on WebSphere Application Server and provides a simple Hello World function. Listing 10. A simple Java-based Hello World Web service
After implementing and deploying the service to the WebSphere Application Server, the WSDL description of the service (Listing 11) defines the SOAP message that you need to pass in to invoke the Hello World service. Listing 11. Snippet from the HelloWorld.wsdl
Using the Web Services JavaScript Library, you can implement a method that invokes the Hello World Service, as shown in Listing 12. Listing 12. Using WS.Call to invoke the HelloWorld Service
You can then invoke the Hello World service by calling the Listing 13. Calling the sayHello function
A successful call will yield the result illustrated in Figure 3. Running this example in Mozilla, Firefox, and Internet Explorer should all yield the same results. Figure 3. The Hello World Example in Firefox ![]()
The Web Services JavaScript Library can be used to incorporate basic SOAP Web services into your Web applications in a simple, browser-independent manner. In the next installment of this series, you can explore the use of the libary to invoke more advanced Web services based on the WS-Resource Framework family of specifications as well as explore ways in which the Web services capabilities can be expanded and integrated into a Web application. |