A hands-on tour of SOAP::Lite

Perl developers looking for a SOAP toolkit can take advantage of one of the more complete toolkits on the market today: SOAP::Lite. Follow one developer as he learns the ins and outs of the toolkit by using it to create a SOAP client.

Choosing a SOAP toolkit can be a daunting task. Web services are still rather young, and each toolkit offers different levels of support for the array of Web services standards.

For Perl developers, however, there is but one real choice: SOAP::Lite. This freely available Perl module, written by Pavel (Paul) Kulchenko, has been widely adopted and is actually one of the more complete toolkits on the market today. But while easy to use, it's still young. Its documentation is incomplete, there are still a few bugs lurking in the code, and at times it can be quite frustrating.

This article will describe my experiences with creating a SOAP client using the SOAP::Lite toolkit. I'll share my train of thought as I worked through a simple problem for the first time using the toolkit, and I'll tell you what I learned, liked, and disliked about SOAP::Lite. With any luck, I will spare you a few headaches.

Service detailsThis example uses a horoscope service I found on XMethods. I chose the service for the simplicity of its interface and because after trying to post a message to it as a test, it did not prove to be a trivial task. The service is an RPC-style Web service and supports only one method, called getHoroscope. I did not want to pick something too complex and get bogged down by details; what is important here is learning SOAP::Lite fundamentals.

Listing A illustrates the simplicity of the interface. It is an example request/response pair to the Horoscope Web service:

Client detailsNow, let's get into some hacking. As you can see, the SOAP::Lite API is rather simple. In fact, you can compose, post, and receive a response to a SOAP message in the fewest number of lines I have ever seen in a SOAP client. Listing B shows a simple example.

On the second line in Listing B, the uri() subroutine identifies the namespace of the SOAP Body. On the third line, the proxy() subroutine specifies the URL to post the message to. On the fourth line, the user invokes a method using the name implemented by the remote Web service. This line actually causes SOAP::Lite to transmit the SOAP message you built to the Web service. Finally, the result() subroutine on the fifth line returns the response object.

SOAPAction HTTP headerMany Web services require, or at least implement, the SOAPAction header. The SOAPAction header is intended to signify the method that is being called in the message so that the recipient service can determine the designated handler without having to parse the SOAP XML.

The horoscope client does not use the SOAPAction header, but I wanted to demonstrate how you can override the default SOAP::Lite SOAPAction header anyway. By using the on_action() subroutine, you can override the default SOAP::Lite behavior for setting the SOAPAction header. Just create an anonymous (or named) subroutine and pass that as an argument to on_action(). The value returned by that subroutine is used in the SOAPAction HTTP header. Listing C demonstrates its usage.

Nested SOAP Body elementsSOAP::Lite actually makes it relatively easy to create complex SOAP Bodies, but you wouldn't know it from its documentation. I managed to get this to work only after posting a message to the SOAP::Lite mailing list.

To give you an idea of what it took to come up with a workable solution, I'll walk you through my thought process as I tried various approaches. I've outlined the steps I took and the guesses I made as I tried to figure out how to construct a method call that had three levels of nested elements.

Here is the method call I was trying to create:<getHoroscope><astrology><sign>Aries</sign></astrology></getHoroscope>

At first glance, I could not figure out how to do this. While it was quite clear to me how to construct a simple RPC call with no nested elements, it was not at all clear how to do something more complicated. However, for the sake of discussion, let's assume for a second that the method call is as follows:<getHoroscope><sign>Aries</sign></getHoroscope>

Constructing this SOAP::Lite call is trivial. (I got it straight from the SOAP::Lite man page). Listing D shows the call.

Of course, the interface calls for a blasted "astrology" element. I don't think that makes a lot of sense from an interface perspective, but that is a topic for a whole other how-to. Anyway, my first thought was to embed SOAP::Data elements, as shown in Listing E.

If you did not notice already, the "astrology" element got swallowed somehow. That's no good. My next hack at the problem produced the code shown in Listing G. (Notice the square brackets indicating an array.)

This lovely bit of code came really close, but it also caused SOAP::Lite to serialize my nested elements as an array, which made the Web service on the other end croak. This actually demonstrates something really cool about SOAP::Lite: its ability to serialize Perl variable types into SOAP::Data data types. It produced the code shown in Listing H.

I tried the same logic using curly brackets, and as I expected, SOAP::Lite serialized the SOAP Body into a hash array. Again, not what I wanted. I was pretty annoyed at this point, to tell you the truth. I searched the Net and combed the SOAP::Lite perldocs, but I found nothing that would help.

I thought for a second that I would need to create my own SOAP::Serializer or something, which, judging by the online documentation, appeared to be an equally daunting task. In a last-ditch effort, I went to the mailing list. The first reply I got (within a couple of minutes) suggested that I produce the code in Listing I.

Unfortunately, this did not help me. But it did show me another cool feature. SOAP::Lite serialized the hash into a SOAP Struct, demonstrating how easily you can create such element types. The code in Listing I produced the output in Listing J.

As you can see, this got me much closer—the nested elements were not being serialized as arrays or hashes, at least. But now I had this weird "c-gensym3" element. Luckily, the next response to my mailing list posting solved the problem.

Attempt #3As it turns out, my first hunch was correct except for one minute detail: The leading slash mark before the nested SOAP::Data element, which you can see in the results shown in >Listing K. It just goes to show you that SOAP::Lite is more intuitive than you might think, if you know enough Perl and have the patience to dive into the source code.

Even more complex data structuresOkay, I finally figured out how to get SOAP::Lite to output what I wanted. So what about more complex structures? Now that you get the idea, let's quickly run through another example so that this how-to doesn't leave you hanging. Suppose we wanted to create the hypothetical SOAP Body shown in >Listing L.

The code from >Listing M is required to achieve the output displayed in Listing L.

ConclusionSOAP::Lite is a great toolkit. It's one of the few that implements and supports SOAP version 1.2, and in my experience, you can get up and running using Web services quickly using it in both server and client contexts. Even so, SOAP::Lite has a long way to go. At the time of this writing, the latest version of SOAP::Lite was 0.52, with lots of bug fixes and changes queued up to go. Unfortunately, releases have been relatively infrequent. The documentation is pretty decent considering it is just one guy doing all of it, but more extensive documentation and examples would go a long way to making SOAP::Lite easier to use.