Recently I was tasked with integrating systemA with systemB. Could I be more vague? Yes, probably so. Anyway, systemB is a vendor’s system which is implemented with both a SOAP and a REST API. REST is inherently easier to implement given its use of URLs. However, I chose to use SOAP, because I figured that in my field I was far more likely to encounter a vendor who only implemented SOAP than I was a vendor who only implemented REST. That decided I set off down a long and SOAPy road…
So, a quick primer. SOAP stands for Simple Object Access Protocol it utilizes XML messages to exchange data between a client and a server. So lets jump into the code.
[sourcecode language=“perl”] #!/usr/bin/perl -w use strict; use warnings; use SOAP::Lite;
Set SOAP username
my $USER = “user”;
Set SOAP password
my $PASSWORD = “password”;
Set SOAP API URL
my $SERVICE_LOC = ‘https://some.soapy-service.somedomain’;
XML namespace
my $SERVICE_NS = ‘http://name.space.domain/widget';
we use this lat
The username and password are set by overriding the
SOAP::Transport::HTTP::Client::get_basic_credentials method
Authentication
sub SOAP::Transport::HTTP::Client::get_basic_credentials { return $USER => $PASSWORD; } [/sourcecode]
I had quite a bit of trouble working out the authentication piece despite how simple it looks enjoy the fruits of my search.
[sourcecode language=“perl”]
prototypes
sub method1; sub method2;
CONNECT TO SERVICE
my $Service = SOAP::Lite -> proxy ($SERVICE_LOC);
$Service->outputxml($OUTPUT_XML); [/sourcecode]
Connecting to the service. Pretty simple here. We also apply the option for XML output which can be set for debugging purposes.
[sourcecode language=“perl”]
Invoking Calls
print “\nHey does SOAP work?\n”;
method1 Test
print “==> Invoking method1” my $result1 = method1($Service); if($OUTPUT_XML = ’true’){ print $result1; } else { if($result){ for my $t ($result->valueof(’//tag/subtag’)) { print $t->{value1} . " - " . $t->{value2} . “\n”; } } else { print “no SOAP for you”; } }
method2 Test
my %DataStructure1 = ( ‘data1’ => ‘John Doe’, ‘data2’ => ‘1234’, ); my %DataStructure2 = ( ‘data1’ => ‘Jane Doe’, ‘data2’ => ‘4321’, ); my $result2 = method2($Service, %DataStructure1, %DataStructure2); if($OUTPUT_XML = ’true’){ print $result2; } else { if($result2){ for my $t ($result2->valueof(’//tag/subtag’)) { print " " . $t->{value1} . " - " . $t->{value2} . " - " . $t->{subsubtag}{value3} . “\n”; } } else { print “no SOAP for you”; } } [/sourcecode]
Big lesson here. Can’t do much with the result if you don’t know how to access it. As it turns out the result from a SOAP call is a SOM object which is a hash of hashes. Once you know this, accessing the data is trivial. See the following XML for an example of how the returned XML looks. [sourcecode language=“xml”] something something else
[sourcecode language=“perl”]
Accessing Functions
sub method1{ my $SOAP = shift; print “\n==> Invoking call method1\n”;
my $URIs; my $SOM = $SOAP->method1(’’);
if($SOM){ if($OUTPUT_XML eq ’true’){ return $SOM; }elsif($SOM->fault) { die $SOM->faultstring; }else{ return $SOM; } }
return 0; }
sub method2{ my $Service = $[0]; my $DataStructure1 = $[1]; my $DataStructure2 = $_[2]; print “\n==> Invoking call method2\n”;
my $Structure1 = SOAP::Data->name(‘structure1’)->value([ SOAP::Data->name(‘data1’)->value($DataStructure1->{‘data1’}), SOAP::Data->name(‘data2’)->value($DataStructure1->{‘data2’}), ]);
my $Structure2 = SOAP::Data->name(‘structure2’)->value([ SOAP::Data->name(‘data1’)->value($DataStructure2->{‘data1’}), SOAP::Data->name(‘data2’)->value($DataStructure2->{‘data2’}), ]);
my $Meth = SOAP::Data->name(‘method2’)->uri($SERVICE_NS);
my $SOM = $Service->call($Meth, $URI, $Location);
if($SOM){ if($OUTPUT_XML eq ’true’){ return $SOM; }elsif($SOM->fault) { die $SOM->faultstring; }else{ return $SOM; } }
return 0; } [/sourcecode]
In this last section we find what probably took me the longest time to figure out. In method1 you can see that you can call methods directly. This works for methods that require no arguments, but I was unable to make it work with arguments at least the implementation I tested. In method2 you can see an alternate method that while more verbose is a little more organized. Of particular note here is that when using call() you may need to specify the uri().
Whew! Glad that’s over. Really though your mileage may vary since SOAP implementations are about as varied as every other vague(read extensible) standard. I hope this guide gets you a little bit closer to implementing your SOAP client.
Resources that you will probably find useful.