Enterprise Software

Enhancing your SOAP::Lite Web service

Developing a simple Web service with SOAP::Lite is straightforward, but a robust service requires working with parameters, accessing SOAP headers, and more. Find out more about using this toolkit to improve your Web services.


As a seasoned Perl hacker and Web services guru, I was amazed at how little effort it took to get up and running using SOAP::Lite. I can’t think of another major SOAP toolkit that is as easy to use, quick to deploy, or flexible. It doesn't take long to assemble the fundamental building blocks of your Web service with SOAP::LITE, and after you do, you can explore using the toolkit's other features. In the examples below, I will show you how to:
  • ·        Access a parameter element by name.
  • ·        Access a SOAP Header element in a request.
  • ·        Add a SOAP Header to a response.
  • ·        Generate a SOAP Fault.
  • ·        Generate a response using Document Style Encoding.

Accessing a SOAP Body element by name
By default, SOAP::Lite deserializes the SOAP request method's data elements and places them into a generic array. This array is passed to the request handler's subroutine as is. The request handler's subroutine can then access those parameters just as it would any other parameterized input. Doing so makes it very easy to prototype various services, but it may cost you some flexibility. If your method has any optional parameters, if it has any attributes that you need to access, or if the parameters passed to your method are not order dependant, this added level of simplicity will most likely be more trouble than its worth.

SOAP::Lite provides a more convenient way for you to access elements in the request by name. This gives you added flexibility in the types of service interfaces you can define. The following example shows you how this is accomplished:
1 #!/usr/bin/perl
2 # Filename: Echo.pm
3 package Echo;
4 use strict;
5 use vars qw(@ISA);
6 @ISA = qw(SOAP::Server::Parameters);
7 sub echo {
8 my $self = shift;
9 my $envelope = pop;
10 my $str = $envelope->dataof("//echo/whatToEcho")->value;
11 return $str;
12 }
13 1;


By extending the SOAP::Server::Parameters package on lines 5 and 6, you can mine the SOAP envelope for data elements by name. To access the SOAP Body, first pop off the SOAP envelope object that is passed invisibly to your method. Then, make a call to the dataof() subroutine to return a SOAP::Data object corresponding to the element name/XPath you specified, as is done on line 10. Because dataof() returns a SOAP::Data object, you have access to all of SOAP::Data's subroutines, including value(), name(), type(), uri() and attr() to gain direct access to the value of the element, name of the element, data type of the element, uri of the element, and all of the element's attributes respectively.

Accessing Header data in the request
The code changes you'll need to access a SOAP Header element in the request are virtually identical to the code changes needed to access a SOAP Body element by name. To access a header element, simply make a call to the headerof() subroutine like this:
7 sub echo {
8 my $self = shift;
9 my $envelope = pop;
10 my $header = $envelope->headerof("//MyHeaderElementName")->value;
12 # do something with the header here, if necessary
13 my $str = $envelope->dataof("//echo/whatToEcho")->value;
14 return $str;
15 }


As with the dataof() subroutine, the headerof() subroutine returns a SOAP::Data element giving you access to the name, value, attributes, namespace, and data type of the referenced header element.

Doc style response
In the examples above, the SOAP response has been using SOAP::Lite's default SOAP encoding. If you have run any of the above examples with debugging turned on, you will no doubt have noticed the seemingly meaningless element names found in the SOAP Body of the response. For example, look at the sample SOAP response below and notice the element named s-gensym3.
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENC=http://schemas.xmlsoap.org/soap/encoding/
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<namesp1:echoResponse xmlns:namesp1="urn:Echo">
<s-gensym3 xsi:type="xsd:string">Echo this statement</s-gensym3>
</namesp1:echoResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>


If the service you're developing requires that this element be specifically named, or that the SOAP Body in the response contain more complex data structures, simply modify your request handler to return a SOAP::Data object, as opposed to a primitive scalar value. SOAP::Lite will automatically serialize the returned SOAP::Data element into its corresponding XML representation. This gives you complete control over the output from your method.
1 #!/usr/bin/perl
2 # Filename: Echo.pm
3 package Echo;
4 use strict;
5 use vars qw(@ISA);
6 @ISA = qw(SOAP::Server::Parameters);
7 sub echo {
8 my $self = shift;
9 my $envelope = pop;
10 my $str = $envelope->dataof("//echo/whatToEcho")->value;
11 return SOAP::Data->name("whatWasEchoed" => "$str");
12 }
13 1;


Composing complex XML data structures using SOAP::Lite isn't always intuitive. For more information on composing complex XML data structures using SOAP::Lite, take a hands-on tour of SOAP::Lite with this article.

Adding a SOAP Header to the response
Often Web services need to add a SOAP Header to the response returned to the caller. This is typical when a Web service is in the role of an intermediary, but technically any acting service is allowed to add Headers to the response. Luckily, adding Headers to the response is as easy as returning an element by name in the SOAP Body. Simply return a SOAP::Header element the same as you would return a SOAP::Data element. SOAP::Lite will automatically serialize each SOAP::Header object and add them to the response in the same order in which they are returned. For example, the following code sample:
1 #!/usr/bin/perl
2 # Filename: Echo.pm
3 package Echo;
4 use strict;
5 use vars qw(@ISA);
6 @ISA = qw(SOAP::Server::Parameters);
7 sub echo {
8 my $self = shift;
9 my $envelope = pop;
10 my $str = $envelope->dataof("//echo/whatToEcho")->value;
11 return SOAP::Data->name("whatWasEchoed" => "$str"),
12 SOAP::Header->name("echoHeader1" => "something"),
13 SOAP::Header->name("echoDate" => time());
14 }
15 1;


Will result in the subsequent SOAP response:
 
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Header>
<echoHeader1 xsi:type="xsd:string">something</echoHeader1>
<echoDate xsi:type="xsd:int">1019579390</echoDate>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<namesp1:echoResponse xmlns:namesp1="urn:Echo">
<whatWasEchoed xsi:type="xsd:string">Echo this statement</whatWasEchoed>
</namesp1:echoResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>


Generating a SOAP Fault
There are many SOAP Faults that SOAP::Lite generates for you. For example, if someone were to invoke a method that wasn't defined in the dispatch handler, SOAP::Lite would return SOAP Fault with a fault string that said something like Failed To Locate Method (Foo) In Class (Echo). There are other SOAP Faults that SOAP::Lite generates for you automatically, many of which you'll find when experimenting with SOAP::Lite. Unfortunately, there are simply too many to list here.

But what about application errors that SOAP::Lite doesn't already know how to catch and return SOAP Faults for? Seasoned Perl programmers who are familiar with Perl's die() function, which terminates the Perl program and outputs the specified error string to stderr, will find it very easy to generate SOAP Faults from their Web services. By simply using the die() function within the context of a service, SOAP::Lite automatically generates and returns the specified error string as a SOAP Fault.

Passing a simple error string to die() by itself does not produce the most informative output. However, by passing a SOAP::Fault object to the die() function, one can set any or all of these values: the fault code, fault string, fault actor, and fault details. SOAP::Lite will automatically serialize the SOAP::Fault object into a valid SOAP Fault. The following code sample can be used anywhere within a request handler to return the corresponding SOAP Fault:
die SOAP::Fault->faultcode('Server.MyFaultCode')
->faultstring('The echo service died')
->faultdetail(bless {code => 1} => 'BadError')
->faultactor('http://$YOUR_NAMESPACE_$HERE');


Resulting SOAP Fault 
<xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/1999/XMLSchema"
xmlns:namesp1="http://namespaces.soaplite.com/perl">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode xsi:type="xsd:string">SOAP-ENV:Server.MyFaultCode</faultcode>
<faultstring xsi:type="xsd:string">The echo service died</faultstring>
<detail>
<BadError xsi:type="namesp1:BadError">
<code xsi:type="xsd:int">1</code>
</BadError>
</detail>
<faultactor xsi:type="xsd:string">http://$YOUR_NAMESPACE_$HERE</faultactor>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>


Even a good toolkit needs some work
Despite the respect I hold for this toolkit, SOAP::Lite has a long way to go. It has yet to natively and completely support SOAP with Attachments, and it is somewhat of a memory hog. But most of all, its documentation needs a lot of work.

There is an online SOAP::Lite User Guide, Cookbook, and of course the man pages, but you often have to shuffle among all three several times before finding the information you're looking for. However, SOAP::Lite makes up for any shortcomings it may have in the way of documentation by having a vibrant and helpful user community. If you ever have questions or can’t figure out how to do something, I encourage you to visit the SOAP::Lite newsgroup, where you can probably find answers to any of your questions. Chances are Paul, the author of SOAP::Lite, will answer your questions directly, which is a testament to how much effort he puts into this toolkit.

Final clean up and consolidated code sample
The code sample included here integrates all of the topics covered in this article into a single program. Additionally, it consolidates the request handler and request dispatcher into a single file making it much easier to maintain.

 

 
 

Editor's Picks

Free Newsletters, In your Inbox