A hands-on tour of SOAP::Lite

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 details
This 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 details
Now, 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 header
Many 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 elements
SOAP::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:
="">
<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.
However, that code produced the SOAP Body displayed in Listing F.
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 #3
As 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 structures
Okay, 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.
Conclusion
SOAP::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.