« first  « prev  » next  » last 

Jump to:


Using AJAX with SOAP

1. A JavaScript SOAP Client


As most people probably now know, SOAP (or Simple Object Access Protocol) is an RPC and data exchange protocol based on HTTP and XML. Because HTTP requests are used, SOAP is able to penetrate most firewalls and can be happily be used on all sorts of networks. The XML data format makes SOAP perfect for use on disparate platforms and technologies (like Windows-to-Linux or PC-to-Embedded).

The fact that SOAP is HTTP and XML based makes it the perfect "back end" for an AJAX application. As shown on the previous page, dynamix XML data needs to be made available to client-side JavaScript code via an HTTP GET (or POST) request - SOAP provides exactly this.

Here we develop a simple SOAP webservice and show how we can consume this service with JavaScript to create an AJAX application.

2. PaperBased


For this demo I've used a different test database. PaperBased is a simple database of academic papers which I maintain on my website and use for my neverending PhD studies. The database is much smaller than the names database we used in the previous example but has a more complex structure, so we can examine a simple parent/child relationship.

Figure 1 shows the demo application. A list of paper titles is displayed on the left hand side of the page. Data for this list is fetched by client-side JavaScript from a SOAP web service, developed in C# using Visual Studio 2005. When a title is clicked a list of authors for that paper is displayed in the blue box on the right hand side of the page.

This demonstrates an important advantage of AJAX technology: the large papers dataset takes a while to fetch and display and we don't really want to have to refresh the entire page (and thus the list of papers) just to display a very small list of authors. AJAX allows us to request authors from a SOAP web method without refreshing the list of papers, thus making the application much more responsive and saving us loads of bandwidth.
The PaperBased demo web application
Figure 1: The PaperBased demo web application

3. The PaperBased web sevice


Three web methods have been created...
  • PaperCount - returns the number of papers in the database
  • AllPapers - returns all papers in the database
  • AuthorsForPaper - returns the authors who wrote a particular paper

We'll look at the PaperCount method first, as it's by far the most simple of the three.

3.1. PaperCount web method


Firstly, here's the C# code for the PaperCount() web method. A very simple SQL statement is executed and the count value is returned as an integer. All the rest of the hard work is handled for us by Visual Studio - creating the WSDL data, parsing the SOAP request and wrapping up the return value in a SOAP reply.
[WebMethod]
public int PaperCount()
{
    SqlDataAdapter da;

    if (paperBasedDataSet.Tables["paperCount"] != null)
        paperBasedDataSet.Tables["paperCount"].Clear();
    da = new SqlDataAdapter("select count(*) from papers", connection);
    da.Fill(paperBasedDataSet, "paperCount");

    return Convert.ToInt32(paperBasedDataSet.Tables["paperCount"].Rows[0][0]);
}

Information on how to call the method (i.e. how to format the SOAP request) and an example of the XML that will be returned to us are provided by the IDE, which is nice!

Format of SOAP request...
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema"
               xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <PaperCount xmlns="http://logicalgenetics.com/" />
  </soap:Body>
</soap:Envelope>

Format of returned XML data...
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema"
               xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <PaperCountResponse xmlns="http://logicalgenetics.com/">
      <PaperCountResult><!-- Returned integer value --></PaperCountResult>
    </PaperCountResponse>
  </soap:Body>
</soap:Envelope>

3.2. Calling the web method with JavaScript


As with the example on the previous page, our skeleton HTML page has a number of placeholder elements into which the values read from XML data will be placed. A span in our HTML with id="paperCount" is used to display the paper count. The page also has a "load()" method, which is called when the page is initially loaded. Within the "load()" function we make a call to "requestPaperCount()", the code for which is shown below. Note that the "soapEnvelope()" function simply formats a string containing the SOAP envelope as shown above.
function requestPaperCount()
{
  // Call function to create SOAP envelope

  var requestXML = soapEnvelope("PaperCount", "http://logicalgenetics.com/", 0);

  // Create a request object and assign a callback for fetch complete

  paperCountRequest = sendRequest("http://logicalgenetics.com/PaperCount",

                       requestXML, onPaperCountXmlFetched);
}

A callback function "onPaperCountXmlFetched" will be called when the SOAP XML data has been fetched from the server. This method performs the next step, checking the HTTP result code and creating an XML parser to parse the paper count from the SOAP response.
function onPaperCountXmlFetched()
{
  if (paperCountRequest.readyState == 4)
  {     
    if(paperCountRequest.status != 200)
    {
      debug('Paper Count: Request error.  Server said: '
                 + request.status + ' ' + request.statusText);
    }
    else
    {
      // Create XML parser and pass in SOAP response     

      var xmlDoc = paperCountRequest.responseXML;
      var xmlWalker = new XmlWalker(xmlDoc);
      // Assign a callback to match the 'PaperCountResult' element

      xmlWalker.addCallback('PaperCountResult', enterPaperCountResult, 0);
      xmlWalker.setDoneCallback(parserDone);
      // Parse the document

      xmlWalker.walkXmlDocument();
    }
  }
}

The callback "enterPaperCountResult()" will be called when the "PaperCountResult" element is encountered in the SOAP response (see above). This function simply extracts the paper count value and inserts it into the HTML document.
function enterPaperCountResult(element)

    document.getElementById("paperCount").innerHTML = element.firstChild.nodeValue;
}

And that's basic principle of the the AJAX/SOAP application. The other two web methods are slightly more complex, since they both return tabular data, but the basics are the same. The rest of this page will show how these more complex methods work.

4. Fetching tabular data


Any object which is able to persist itself as XML can be returned by a SOAP web method when using VS/C#. Luckily for us, this includes a data table object. We'll now look at how the "AllPapers" web method returns multiple data rows. Other than the fact that it returns more complex XML which is harder to parse, this method is identical to the "PaperCount" method which we've already looked at.

Firstly, here's the C# code for the method...
[WebMethod]
public DataTable AllPapers()
{
    SqlDataAdapter da;

    // if the data table has already been filled, clear it

    if (paperBasedDataSet.Tables["papers"] != null)
        paperBasedDataSet.Tables["papers"].Clear();

    // Fill the data table

    da = new SqlDataAdapter("select * from papers", connection);
    da.Fill(paperBasedDataSet, "papers");

    // return the data table object

    return paperBasedDataSet.Tables["papers"];
}

Very simple indeed! The SOAP response XML is a little more complicated, though, and far too large to show here. The basic principles applied to the paper count method apply here. The page's "load()" function calls another function called "requestAllPapers()" which sets up a SOAP request and assigns a callback method called "onAllPapersXmlFetched". The "onAllPapersXmlFetched" method creates an XML parser and assigns callbacks to deal with various elements in the SOAP response. The results are added to a specific div element in the HTML document.

5. The "AuthorsForPaper" method


This method differs from the others in two important ways: firstly, it takes a parameter, the ID of the clicked paper; secondly, it is not called when the page is first loaded, but when a paper title is clicked by the user.

5.1. Sending a parameter


Here's the C# code for the web method...
public DataTable AuthorsForPaper(int paperId)
{
    SqlDataAdapter da;
   
    // Clear the table if it's already full

    if (paperBasedDataSet.Tables["papers"] != null)
        paperBasedDataSet.Tables["papers"].Clear();

    // Set up SQL using paperId param

    string sql = "select A.author_id, A.firstname, A.surname from ";
    sql += "authors A join written W on A.author_id = W.author_id ";
    sql += "where W.paper_id = @pid"
    da = new SqlDataAdapter(sql, connection);

    // Add a param and assign a value to it

    da.SelectCommand.Parameters.Add("@pid", SqlDbType.int);
    da.SelectCommand.Parameters["@pid"].Value = paperId;

    // Populate the data table

    da.Fill(paperBasedDataSet, "authors");

    // return the data table

    return paperBasedDataSet.Tables["authors"];
}

Nothing particularly special here. The SQL is slightly more complicated and we have to set up a parameter, but there's nothing special to do to get the input parameter to work, we just treat it like any other argument.

The SOAP request needs to be a bit more complex to call the method. Here's what it should look like now...
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema"
               xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <AuthorsForPaper xmlns="http://logicalgenetics.com/">
      <paperId><!-- Integer goes here! --></paperId>
    </AuthorsForPaper>
  </soap:Body>
</soap:Envelope>

5.2. Fetching the data


Each paper title is within an anchor element. The onclick attribute of these elements are set up as follows...
<p>
  <a onclick="javascript: displayAuthors(12);" ...>Paper Title</a>
</p>

The method "displayAuthors()" is called when a paper title is clicked and the unique ID for that paper is passed into the function as a parameter. Since we set up the HTML for the papers list ourselves (in the code for parsing the "AllPapers" results), we can create this JavaScript call in code.

The "displayAuthors()" method is very simple...
function displayAuthors(paper_id)
{
    // Output a "Please Wait" message

    authlist = document.getElementById("authlist");
    authlist.innerHTML = "<li>Loading authors for " + paper_id + "...</li>";

    // Send request for authors

    requestAuthorsForPaper(paper_id);
}

Here's the code for the "requestAuthorsForPaper()" method. Nothing very different here, except that we pass a parameters array into the "soapEnvelope" function (which is too large to show here but quite simple, using the parameters array to create the SOAP envelope string).
function requestAuthorsForPaper(paper_id)
{
  var params = Array();
  params["paperId"] = paper_id;

  // Create SOAP envelope, passing in params array

  var requestXML = soapEnvelope("AuthorsForPaper",
                          "http://logicalgenetics.com/", params);

  // Send the request (and assign callback)

  authorsForPaperRequest = sendRequest("http://logicalgenetics.com/AuthorsForPaper",

                       requestXML, onAuthorsXmlFetched);
}

When the data comes back, the "onAuthorsXmlFetched()" callback function is called...
function onAuthorsXmlFetched()
{
  if (authorsForPaperRequest.readyState == 4)
  {     
    if(authorsForPaperRequest.status != 200)
    {
      debug('Authors: Request error.  Server said: '
               + request.status + ' ' + request.statusText);
    }
    else
    {
      // Create XML parser and pass in SOAP response     

      var xmlDoc = authorsForPaperRequest.responseXML;
      var xmlWalker = new XmlWalker(xmlDoc);

      // Lots of callbacks to add here

      xmlWalker.addCallback('surname', enterSurname, 0);
      xmlWalker.addCallback('firstname', enterFirstname, 0);
      xmlWalker.addCallback('author_id', enterAuthorId, 0);
      xmlWalker.addCallback('authors', 0, exitAuthors);
      xmlWalker.addCallback('AuthorsForPaperResult', enterAuthorsForPaperResult, 0);
      xmlWalker.setDoneCallback(parserDone);

      // Parse the document

      xmlWalker.walkXmlDocument();
    }
  }
}

With the exception of "exitAuthors", all of the XML parser callback methods take the following format, simply storing the a data item in a global variable.
=javascript]function enterSurname(element)
{
    newSurname = element.firstChild.nodeValue; 
}

The "exitAuthors" function is called when the XML parser reaches the end of an author element. By this time, all the appropriate data should have been placed in the appropriate global variables. We can now take that data and insert it into the HTML document.
function exitAuthors(element)
{
    li = document.createElement("li");
    li.appendChild(document.createTextNode(newFirstname + ' ' + newSurname));
    document.getElementById("authlist").appendChild(li);
}

6. AJAX and SOAP: Done!


We've hopefully now covered the ins and outs of consuming a SOAP web service with JavaScript to create an AJAX application.
  • Writing web methods in Visual Studio
  • How SOAP requests and responses are formatted
  • Passing parameters
  • Parsing the resulting SOAP response

On the next page is a brief description of the XML parsing class that's been used heavily in this article, but has so far been a bit of a black box. The last page then contains some very brief conclusions and links to code and data downloads.

« first  « prev  » next  » last 

Jump to: