Simulate SOAP and Web Services

2002-9-11 13:24:56【作者】 AMTeam.org 【进入论坛】
广告

Simulate SOAP and Web Services


Use the XmlHttpRequest COM object to exchange XML data over HTTP.

by Dino Esposito

XML and SOAP are two hot topics, and with good reason. The XML (Extensible Markup Language) format is widely accepted as a sort of new and more powerful ASCII table. And the SOAP (Simple Object Access Protocol) distributed computing technology boasts the cross-platform capabilities, flexibility, and ease of use found in HTTP and XML. In fact, you could say SOAP technology was an inevitable "next step" following the broad acceptance of HTTP and XML. Here's why I say that.

What you need:

Visual C++ 6.0

MSXML 2.5 and later or IE 5.0 and later

Personal Web Server or IIS 4.0 and later  
 
The HTTP networking protocol ensures worldwide connection; it's an "anytime, anywhere, on any device" low-level protocol that lets you connect to any platform. Over that connection, though, you need to exchange information with your remote interlocutor, no matter what character sets are involved or what format the text assumes. The XML markup meta-language is the ideal tool for that job. XML is text-based, easy to read and understand, and its tags allow you to identify and mark specific pieces of text associated with a precise meaning. Once you have a universal transportation layer such as HTTP and a widely spoken lingua franca such as XML, you can leverage both to create a universally accepted RPC method. That's the essence of SOAP.

As Davide Marcato explained in his article in the April 2000 issue of VCDJ, SOAP can be considered an HTTP special edition for remote method invocation (see Resources). With today's technology, you can write a pair of proxy/stub modules to prepare, send, and receive SOAP payloads—and that's exactly what several companies, including Microsoft and DevelopMentor, are doing. Most of us, however, are waiting on the development of some type of object model or a simplified API—as we saw in the evolution of HTTP—to make SOAP useful on a broad scale.

Meanwhile, you already might be facing the problem of setting up XML-based communications between remote sources. SOAP is a key element in the next generation of COM components, called Web Services because of their ability to fulfill XML-based requests coming through HTTP from a platform-independent client. If you plan to use SOAP—and you should—you might want to check out the VC++ SOAP toolkit available from the MSDN home page.

But in addition to that toolkit, I suggest you consider a little known but free runtime component called XmlHttpRequest. It comes as a constituent part of the Microsoft XML parser (MSXML) and, as such, can be redistributed as a separate component with your applications. In this article, I show you how to use XmlHttpRequest to exchange any sort of information with remote URLs: no matter what platform or language is involved. Before you begin working with SOAP, you should take a closer look at this useful component.

Manage HTTP Packets

The XmlHttpRequest COM component has been available since IE 5.0 was released in March 1999. This component wasn't designed specifically to work with SOAP, but it works well as a client-side proxy for talking to a remote URL. And, XmlHttpRequest embodies the essence of SOAP and Web Services.

XmlHttpRequest is not as cross-platform-capable as a real Web Service client should be. In fact, the component works best when communicating with URLs that are ASP pages. XmlHttpRequest requires a COM-aware client, and it can't yet be used for server-to-server communications (more on this later). However, if you're writing a Win32 or Web application today and can afford a client-side COM object to manipulate the HTTP low-level stuff, XmlHttpRequest is your best tool for the job.

Primarily, XmlHttpRequest lets you send HTTP packets to a specified URL, both synchronously and asynchronously. On top of this core functionality, you can build any object model you need, including a SOAP object model. Frankly speaking, I expect Microsoft to develop a future version of XmlHttpRequest or a similar object that allows you to call remote methods through a natural programming interface and take advantage of SOAP under the hood. (No, I don't consider MyObject.asp?method=Foo to be a natural programming interface!)

Working with XmlHttpRequest involves two basic steps, familiar to anyone who knows a bit of WinInet. (Later in this article, I'll talk about how these steps represent a significant drawback for the XmlHttpRequest object.) The first step: Provide the component with the minimal information it needs to get in touch with the URL. To begin, open the remote connection by specifying the URL, the HTTP command (GET, POST, PUT, and so on) to be issued, and whether you want the connection to run synchronously or asynchronously (the default). To wrap up the first step, enter any user ID and password necessary for authentication purposes; if you don't provide such security information for a Web server that requires it, a logon dialog box asks you to enter a username and password before continuing (see Figure 1).

   
Figure 1. Log Onto the Site. Click here.

 
The second step consists of sending the information to the URL through HTTP. In this step, you specify the content of additional headers and the body of the HTTP command. If you've requested synchronous behavior, your application waits until the invoked URL responds. In asynchronous connections, your code continues executing the next line but the object makes available a property (readyState) for you to loop on waiting for the response to arrive.

When the HTTP command terminates, the application can get output results in a variety of formats. The output is available through different properties as raw text, an array of unsigned chars, an IStream pointer or an XML object model (XMLDOM). This list of available output formats reveals the XmlHttpRequest component's nature as an HTTP object model with special support for XML data.

Typically, programmers use XmlHttpRequest to ask an aware Web page to return a configurable excerpt of its information. The caller can then use the information to refresh or populate its user interface. In other words, XmlHttpRequest allows you to consider any Web site or server-side application accessible over HTTP as a sort of external, cross-platform library. Although XmlHttpRequest has nothing to do with SOAP, you can use it to simulate SOAP functionality. You also can use it to put together a high-level object model to invoke methods on any remote object, including ASP pages; CGI applications written in Java, Perl, C, or even Visual Basic; ISAPI DLLs; or Java servlets.

XmlHttpRequest in Action

The XmlHttpRequest automation COM object is typically used from script applications. The progID is Microsoft.XmlHttp and the library that contains it is msxml.dll. Here's how you use the component from a VBScript script to issue a synchronous POST command to a specified ASP page:

url = "http://expoware/test.asp"

Set http = _

   CreateObject("Microsoft.XmlHttp")

http.open "POST", url, False

http.send ""

MsgBox http.responseText

In this example, the HTTP request has an empty body—the only argument of the send() method. You use the open() method to initialize the component on a URL, a command, and a working mode. The open() method can take two more arguments—the username and password required to log on to a protected Web site:

http.open(Method, Url, Async, User, Pswd)

The actual conversation begins only when you call the send() method:

http.send(body)

Send() takes a single Variant argument representing the body of the HTTP request. Under the umbrella of the Variant datatype, the argument can be a BSTR, a safearray of bytes, an IDispatch pointer to one XMLDOM, or an IStream pointer. When the input type is BSTR, the return data is always encoded as UTF-8, and the caller must set a Content-Type header with the appropriate content type and include a charset parameter. To set a header, use the setHeaderRequest() method:

http.setRequestHeader "ContentType", _

      "text/html;charset=UTF-8"

http.send(body)

You can use the same method to send a custom header with custom information.

If the operation takes place synchronously, the instruction following send() executes only when the response from the URL has been completely received. Otherwise, you should wait for the value of the readyState property to become 4, which means "completed."

The response you get is everything the URL writes to its stdout console. If the URL is an ASP page, you get back every single byte, written through Response.Write. For example, suppose test.asp is the next simple server page:

<%

  ' test.asp
  
Response.Write "Hello, world"

%>

The final MsgBox would print out "Hello, world".

If the URL is a CGI application, you get back all information the application itself prints out. For example, here's an extremely simple CGI program:

// cgitest.c : Entry point for the

// CGI app

#include <stdio.h>

#include <io.h>

int main(int argc, char* argv[])

{
  
char pszBuf[1024];
  
int nLen = read(0, pszBuf,
     
sizeof(pszBuf));
  
pszBuf[nLen] = 0;

printf("200 OK\r\n");
  
printf("Content-Type:
        
text/plain\r\n");
  
printf("Content-Length: %d\r\n",
        
nLen);
  
printf("\r\n");
  
printf(pszBuf);
  
return 0;

}
This program reads up to 1K from stdin (file handle of 0 in function read()) and returns the same content to the caller. Compile the program and copy it to the Web server's Cgi-Bin subdirectory, then modify the script like this:

url = "http://expoware/cgi-bin/cgitest.exe"

Set http =
  
CreateObject("Microsoft.XmlHttp")

http.open "POST" url, False

http.send "Hello, VCDJ"

MsgBox http.responseText

The MsgBox now prints the body of the HTTP request, "Hello, VCDJ".

The response can be available in four different formats: IStream, plain text, an array of bytes, and (when possible) an XMLDOM reference. The XmlHttpRequest component provides four different properties to accommodate those formats: responseStream (IStream), responseText (plain text), responseBody (unsigned bytes), and responseXML (XMLDOM).

The key advantage of XmlHttpRequest is its ability to handle XMLDOM. The component allows you to send and receive XML data over the Web through the document object model. And, it takes care of marshaling the XMLDOM back and forth through the Web server. You can send your IDispatch pointer to XMLDOM through the send() method:

Set http =
  
CreateObject("Microsoft.XmlHttp")

http.open "POST", url, False

Set xml =
  
CreateObject("Microsoft.XmlDom")

xml.loadXML "<one>Hello</one>"

http.send xmldom

If the target URL is an ASP page, the IStream support built into the ASP Request object allows you to do something like this:

<%
  
Set x =
   
CreateObject("Microsoft.XmlDom")
  
x.load Request

 ' TODO:
  
' The XMLDOM has been initialized. 
 
' Now process the XML data stream
  
set node =
     
x.selectSingleNode("one")
  
node.text = "Hello, VCDJ"

 ' Send back XML content
  
Response.ContentType = "text/xml"
  
Response.Write x.xml
  
Set x = Nothing

%>

You initialize a server-side instance of XMLDOM with the Request object's content. You then process the XML data as needed and use Response to send the modified stream back to the caller. Be aware of this caveat, though: You must explicitly set the content type to text/xml and write to stdout the XML source code, not the XMLDOM IDispatch pointer! In fact, don't forget that Response is simply a facility built over HTTP. The ASP page can't know whether the client is capable of handling a special and nonstandard datatype such as an IDispatch pointer to XMLDOM.

XmlHttpRequest transforms the XMLDOM into plain XML before contacting the URL, then (if possible) transforms any XML content it gets back into an XMLDOM reference (see Figure 2). The client application can obtain the modified XMLDOM through the responseXML property. If the content returned is not a well-formed XML stream, the property is set to NULL.

   
Figure 2. Transforming XMLDOM Objects. Click here.

 
You can write CGI applications for any platform with a number of different languages, including Perl and Java. Just be sure to have an XML parser on the chosen platform to process the XML text you receive, and remember to set the Content-Type header properly:

printf("Content-Type: text/xml\r\n");

Using the XmlHttpRequest object on the client side enables you to communicate with any module running on any platform reachable through a URL. You can send any XML packet to this URL, including SOAP payloads. Is this a completely new functionality? Not exactly, because WinInet or sockets have been around for some time. But I think you'll find XmlHttpRequest extremely handy due to its usability from both script and C++ applications. To appreciate the power of this object, look at XmlHttpRequest in a real-world setting.

Power Up XmlHttpRequest in Real-World Apps

You can use XmlHttpRequest to easily arrange calls to libraries of data and code located on the Web, and you can build Web sites that make their content available separately from any surrounding graphics, through XML. This approach is much better than scanning the HTML source code of downloaded pages, desperately seeking a known pattern of information.

I used this object recently to build a free viewer of extremely volatile information. I wrote a pretty lean C++ application that connects to an ASP page and downloads all the information it needs to populate the user interface (see Listing 1). The information is displayed in an edit box (see Figure 3). The ASP page was simply a new one I added to the Web site; I made it invisible to and unreachable from the other pages (see Listing 2). Data sources, components, and all the back-office framework remained unchanged.

   
Figure 3. Your Information Order is Ready Click here.

 
To use XmlHttpRequest from C++ code, you can import the library through the #import directive or simply use #include <msxml.h> and create an instance of the object yourself.

To use XML data comfortably with this object, you need an XMLDOM reference. You can modify the code in Listing 1 to create a general-purpose function that returns an XMLDOM whenever available. Instead of using the method get_responseText, use get_responseXML:

pXmlHttp->get_responseXML(

   (LPDISPATCH *) pXmlDoc);


   
Figure 4. Serving XML Data Through an ASP Page Click here.

 At this point, you can use the XMLDOM hierarchy of nodes to extract the data you need. A more complex application uses a Web server and its set of databases to serve XML data about training classes and seminars through an ASP page (see Figure 4). The application sends HTTP GET commands to specify the ID of the selected class and the various topics and subtopics covered in the class. The application exploits a variant of the code in Listing 1 to retrieve an XMLDOM pointer and walks through the XML tree. When you have a viewer like this one at your disposal, you can ship it and let the application go to the Web to get data. How is all this different from having a WebBrowser embedded in a dialog? It's different in the same way that HTML is different from XML. The former is a terrible mixture of graphics, layout directives, and data. The latter is only a description of data that an application is responsible for displaying.

XmlHttpRequest is a great solution for direct Web connectivity from a C++ or script application. It's an object model for HTTP commands that integrates delightfully with the XMLDOM hierarchy. Like anything else, of course, this object has its limitations. Neither XmlHttpRequest nor XMLDOM are useful from within an ASP server-side context (see the sidebar, "Beware of These Server-Side Woes"). And, when you're executing code in an ASP page, and attempt to invoke XmlHttpRequest or try to initialize the XMLDOM with HTTP-based content, you'll experience some security and performance limitations.

ASP is implemented as an ISAPI module that runs under the supervision of MTS and IIS. The ASP ISAPI system runs in a protected server service that ends up breaking some of the basic functionality of the WinInet API on which the HTTP capabilities of MSXML are based. At the moment, there's no easy and practical workaround. According to all reports, this problem has been fixed in the yet-to-come version 3.0 of MSXML. Until that version ships, you need a hand-crafted version of XmlHttpRequest that doesn't rely on WinInet and UrlMon and utilizes a raw socket-based mechanism to arrange the server-to-server communication over HTTP.

Dino Esposito is a freelance trainer and consultant based in Rome. He authored Visual C++ Windows Shell Programming and Windows Script Host Programmer's Reference for Wrox Press and is a regular contributor to MSDN. E-mail Dino at dinoe@wrox.com

如果您希望与本文章的作者或其所在机构,进一步交流,请联系:姜小姐
jill.jiang@amt.com.cn | 021-51096826-112 | 在线联系
企业信息化杂谈[原创]国内企业信息化很难回避..

国内企业信息化所面临的环境与西方企业、外资企业、或者合资企业有很大的不同,这就决定了国内企业信息化有自己的特点。

吕建伟 专栏文档知多少---走出软件作坊:三..

我们也在力求能少写就少写,根据团队的、客户的磨合理解共识程度,哪个文档或哪个环节不需要写,我们就砍掉。

CIO职场,强者生存?

在2008年,我们将继续看到CIO向商业运营方向发展。与此同时,我们也会看到商业管理人员将与技术管理人员一起竞争CIO岗位。 IT领导者的就职机会虽有不少,但其难度将会大幅提高。2……

防震减灾,IT当关

今天,任何的防震救灾体系,都离不开IT技术。地震观测台是数字化的,震害防御需要对以往的地震信息进行数据分析,应急救援要需要现代多样化的通讯技术。如果说,在许多行业,信息技术还只是一……