Exposing Application Services With SOAP
July 12, 2000
Using the MS SOAP Toolkit for Visual Studio
With its recently released SOAP Toolkit for Visual Studio, Microsoft has made the process of creating Windows-based SOAP Web Services easy for anybody familiar with the use of COM objects. There are, however, a few gotchas that you need to know about. This article, and the associated download, will provide you with the information you need to make it work.
Readers are expected to be familiar with the use of Visual Basic to create COM objects, and to have downloaded the Microsoft SOAP Toolkit.
Step 1: Implementing the server code
What you have to know
The MS SOAP Toolkit is a method of exposing server-based COM and Script-based application services to client applications using the SOAP protocol. However, the toolkit just doesn't work with any old COM object you want to use. There are certain limitations and nuances that can make life interesting if you're not familiar with them. The guidelines for creating COM application services for use with the SOAP toolkit are simple:
- Your COM objects must implement the IDispatch interface. The SOAP toolkit has been designed to only access the default dispinterface of a COM object.
- Your methods can only use Automation compatible datatypes. Basically, this means any datatype that VBScript can support with three exceptions: MS SOAP doesn't do objects, arrays, or structs. I'll show you some simple workarounds for these exceptions later.
- ByRef (in/out) parameters must be passed as Variants. This is another limitation of ASP's weak typing architecture. If you plan on using input/output parameters in your methods, you have to use the Variant datatype to do so. This means that you have to be careful in your own code to ensure that the passed in parameters are of the appropriate...and expected datatypes.
- Handle your errors: the MS SOAP toolkit doesn't seem to handle errors raised within COM objects that well at all. Try to avoid firing unhandled errors and exceptions if at all possible. Until this limitation is fixed, if you do need to return detailed error information, use output parameters or method return values to report the necessary information.
Writing the Code
Now that we've covered the basics, it's time to look at some code. We're going to create a simple webservice that performs a 20-minute delayed Stock value query (for what it's worth, this is the same basic sample implemented in the IBM SOAP For Java package, except this time we're using Visual Basic to implement the server components).
Launch Visual Basic and begin a new ActiveX DLL project (MS SOAP only supports the use of COM Inprocess Components). Rename the project file "MSSOAP" and the default class object "Tutorial".
Now, add the following code to the Tutorial class:
Const url As String = "http://18.104.22.168/i3localweb/ProcessQuote.asp?txtQuery=" Private Function processQuote(ByVal Symbol As String, _ ByRef quotedate As String, _ ByRef price As String, _ ByRef change As String, _ ByRef percent As String, _ ByRef volume As String, _ ByRef errinfo As String) As Boolean Dim wt As WireTransfer Dim sResult As String Dim xDoc As DOMDocument30 On Error GoTo ErrorHandler getQuote = False Set wt = New WireTransfer sResult = wt.GetPageByURI(url + Symbol) Set wt = Nothing sResult = Replace(sResult, "xmlns='x-schema:quotesSchema.xml'", "") Set xDoc = New DOMDocument30 xDoc.loadXML sResult If xDoc.xml <> "" Then quotedate = xDoc.selectSingleNode("//date").nodeTypedValue price = xDoc.selectSingleNode("//price").nodeTypedValue change = xDoc.selectSingleNode("//change").nodeTypedValue percent = xDoc.selectSingleNode("//percent").nodeTypedValue volume = xDoc.selectSingleNode("//volume").nodeTypedValue Else quotedate = vbNullString price = vbNullString change = vbNullString percent = vbNullString volume = vbNullString errinfo = xDoc.parseError.reason End If Set xDoc = Nothing getQuote = True Exit Function ErrorHandler: Set wt = Nothing Set xDoc = Nothing errinfo = Err.Description getQuote = False End Function
This code uses the WireTransfer object implemented in the MS SOAP Toolkit to retrieve the current stock price information from the quote server. You may be wondering why I'm not using the Proxy component to execute the processQuote method against the server. The reason is simple: the quote server uses HTTP GET to process the request, not SOAP. The Proxy component only works with SOAP-based requests.
Next, add the following two public methods to the Tutorial class:
Public Function getQuote(ByVal Symbol As String, _ ByRef quotedate As String, _ ByRef price As String, _ ByRef change As String, _ ByRef percent As String, _ ByRef volume As String, _ ByRef errinfo As String) As Boolean getQuote = processQuote(Symbol, quotedate, price, _ change, percent, volume, errinfo) End Function Public Function vargetQuote(ByVal Symbol As String, _ ByRef quotedate As Variant, _ ByRef price As Variant, _ ByRef change As Variant, _ ByRef percent As Variant, _ ByRef volume As Variant, _ ByRef errinfo As Variant) As Boolean vargetQuote = processQuote(Symbol, quotedate, _ price, change, percent, volume, errinfo) End Function
These are the actual methods you will expose as SOAP webservices. Notice that I've defined two different versions of the exact same method--one that uses ByRef String parameters and another that uses ByRef Variant parameters. I've done this to help demonstrate some of the gotchas we'll explore later on when talking about access webservices with the Proxy component. In your real webservices, it is not necessary to expose multiple versions of a single method.
At this point, save your work and build the project. We're ready to publish your component as a SOAP Webservice.
Step 2: Publishing your Webservice
Using the SDL Wizard
The SOAP Toolkit relies on a simple XML-based representation of a COM objects type library to tell it how to build and parse SOAP requests. This Service Description Language (SDL) is the key to publishing Webservices for use by the Microsoft SOAP client tools. For complex webservice applications, writing SDL by hand would be a fairly nontrivial task. Luckily, the SOAP Toolkit comes with a Wizard that does the work for you.
Using the Wizard is simple--tell it which COM DLL you want to publish, select the methods you wish to expose (you can only expose methods contained in the COM objects default interface), tell the wizard what kind of listener you want to use (ASP, ISAPI or Custom), then click Finish. It's really that simple.
Use the Wizard now to generate an SDL document for the Tutorial COM object we just created in Visual Basic. The wizard will ask you for the base URL you'd like to publish the webservice to. This can be any virtual directory on your webserver that contains the listener.asp file that comes with the SOAP toolkit. For the purposes of this demonstration, use http://localhost/soaptutorial/ where localhost equals the name of whatever webserver you're currently using. (Don't forget to add the last "/" at the end; the Wizard won't do it for you and, if you forget it, you'll have problems.)
The SDL Wizard will output two documents: 1) the SDL document that describes your service, and 2) and ASP file (or an .SOD file if you selected the ISAPI-based listener) containing the actual code necessary to call and execute your COM objects methods. Both of these documents are required to publish Webservices using the MS SOAP Toolkit.
(It is important to note that the XML-based Service Description Language used by the SOAP toolkit is currently only supported by the Microsoft toolkit. No other SOAP implementation uses SDL in anyway.)
Setting up the SOAP ASP Listener
Once you've run the Wizard and generated the required SDL and ASP documents (they should be called Tutorial.xml and Tutorial.asp), you're ready to set up your Web environment. Using IIS, create a new Virtual Directory called "soaptutorial". Point the virtual directory to the same physical folder where you had the SDL Wizard store the generated SDL and XML documents. Copy the listener.asp file into the same folder and you're all set--your COM Webservice application has been successfully published to the Internet.
You can test your installation by navigating to: http://localhost/soaptutorial/tutorial.asp.
If all went well, you should receive an XML-formatted response indicating a SOAP Fault Error. This is expected because no actual request was posted for the listener to process.
Which is Better, the ASP Listener or the ISAPI Listener?
You may be wondering why I've only been talking about the ASP-based server components when the toolkit comes with an ISAPI-based listener as well. Isn't ISAPI better than ASP? Shouldn't we be using it instead? The answer is one of personal choice. ASP is a less complex, more flexible route to take than is ISAPI. The two listeners operate fairly similarly (once various bugs are fixed... see the download links at the end of this article), so there is no real compelling reason to choose one over the other.
Step 3: Implementing the Client Code
How it works
Now that your webservice has been successfully published, how do we access and use it? There are basically two methods of consuming SOAP webservices built with the MS SOAP toolkit. 1) by handcoding the SOAP request envelope using the SOAPPackager and WireTransfer objects, or 2) by using the dynamic Proxy object to transparently handle the processing for us. Both approaches have their advantages and disadvantages, so neither technique is any better than the other.
The Proxy component is merely a thin wrapper for using the SOAPPackager and WireTransfer objects directly. It allows you to call methods on a remote server using the same syntax you would use to call any method on any COM object. It does this through a custom implementation of the IDispatch interface. When a method is invoked, the Proxy queries the currently loaded SDL document for information about method and parameter names, data types, etc, packages the appropriate SOAP request, then handles the marshalling back and forth between client and server. This is almost a complete abstraction of the process.
Writing the code
Using the Proxy component, a developer can execute the webservice methods in as little as three lines of code. But there are a few gotchas that you have to know about. Unfortunately, we don't have enough space to outline all of these issues here, but I've done my best to demonstrate and document them in the download associated with this article. The sample contains all the code needed to demonstrate how to use the SOAP Proxy component to call methods on the Tutorial webservice we created earlier.
Step 4: Integrating with the Alphaworks SOAP Project (IBM's SOAP For Java)
One of the primary goals for SOAP is to allow for a greater amount of interoperability between diverse application architectures and platforms. For years, developers have been trying to find ways to get different platforms and programming languages to talk efficiently to each other and share data in a nice, consistent manner. During this time many different solutions have been developed, but most end up being too geared towards one language or platform, too proprietary, too difficult to use, etc.
SOAP, on the other hand, is simple enough and open enough (it is based on XML after all), to be easily accessible from any language or platform. Thus, you are starting to see, or will see, SOAP support in tools all across the market. Microsoft's SOAP toolkit is just one of those tools. IBM's SOAP for Java is another. The downside is, even though both tools implement the same version of the SOAP specification, they do so with enough minor semantic differences that trying to get the two to work together "out-of-the-box" is pretty much like the old worn-out cliche of "trying to fit a square peg in a round hole." Just like the square peg, if you want to get the MS Toolkit working with SOAP for Java, you're going to have to round out the edges a little.
The problem lies in the way the SOAPPackager component builds SOAP request envelopes. It doesn't do it very well. Significant bugs in the output, and what can be described as lazy adherence to the spec, cause nothing but problems when trying to integrate with other, more spec-compliant implementations of SOAP.
So how do we get around these problems? Well, for starters, we fix the bugs. Since the source code for the MS SOAP toolkit was released with the package, I've taken the liberty to fix many of the most glaring bugs. With permission, I've uploaded those changes to my web site for download while Microsoft works on the next release of the kit. You can download these changes at the URL I've provided at the end of this article.
Everything else you need to know about getting the two to talk is documented in the demo download.
Downloads and Further Information
You can download a bug-fixed update for the Rope.dll contained in the SOAP toolkit