SOAP Service and Client Example

Posted by admin on May 6, 2010 in Database |

Server: This test server will query a MySQL database and return tables with names matching the selection criteria.

#!/usr/bin/perl 
#use SOAP::Lite +trace=>"all";
use SOAP::Transport::HTTP;

my $daemon = SOAP::Transport::HTTP::CGI
      ->new (LocalPort => 8080)
      ->dispatch_to('Services') 
      ->handle;

BEGIN {
	package Services;
	use vars qw(@ISA);
	@ISA = qw(Exporter SOAP::Server::Parameters);
	use SOAP::Lite;
	use DBI;
	use Data::Dumper;
	$SOAP::Constants::DO_NOT_USE_CHARSET = 1;

	# sample service to list schemas and tables
	sub listTables {
	        my $self = shift;
	        my $envelope = pop;
	        my $wsdl = ($envelope->{'_content'}->[1]->{'xmlns:tns'} =~ /wsdl/)?1:0;
		my $dbh;
		my $query;
		
		# Grab details
		for (qw/database tablename/) {
		        eval {$envelope->dataof("//$_")->value};
		        &makeFault("Missing or invalid $_") if $@;
		        $query->{$_} = $envelope->dataof("//$_")->value unless $@;
		}        

		# Connect to database
	        my $dbh = DBI->connect ("DBI:mysql:database=mysql","root","xxxxxx", { PrintError => 0, RaiseError => 0 }) or &makeFault($DBI::errstr);

		# Run query
        	my $sth = $dbh->prepare("select table_schema, table_name from information_schema.tables where table_schema like ? and table_name like ?") or &makeFault($DBI::errstr);
        	$sth->execute($query->{'database'}, $query->{'tablename'}) or &makeFault($DBI::errstr);

		my $type = 'xsi:type="xsd:string"';
		my $xmlbody = "";
		while (my @row = $sth->fetchrow_array()) {
			$xmlbody .= "<tableRow>";
			map {$xmlbody .= "<col $type>" . $_ . "</col>"} @row;
			$xmlbody .= "</tableRow>\n";
		}
		
		my $xml = "
		<response>
		    <table>
			<colHeading $type>Database</colHeading>
			<colHeading $type>Table Name</colHeading>
			<table>$xmlbody</table>
		    </table>
		</response>";

		# Return different content depending on how its called
   		my $result = ($wsdl)
   			?SOAP::Data->name('response')->type('string')->uri('')->value($xml)
   			:SOAP::Data->type('xml' => $xml);
        	return $result;
	}
      
	sub makeFault {
		my ($faultString) = @_;
		die SOAP::Fault->faultcode("Services")->faultstring($faultString);
	}
}

Client: This test client will query the server and dump the response

use SOAP::Lite;
#use SOAP::Lite +trace=>"all";
use Data::Dumper;
use strict;

my $client = SOAP::Lite->proxy("http://localhost:8080/soap/test.pl")->uri("Services");
my $class =
{
	database => 'information%', 
	tablename => 'column%',
};

my $soapResponse = $client->listTables(
	SOAP::Data->name('listTablesDetails' => $class)
);
checkResponse($soapResponse);
print Dumper($soapResponse->result);


sub checkResponse() {
	my ($soapResponse) = (@_);
	if ($soapResponse->fault)
	{
		print $soapResponse->faultcode, ": ", $soapResponse->faultstring, "\n";
		exit();
	}
}

WSDL: This WSDL is compatible with .NET

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://localhost/wsdl/Services.wsdl" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsd1="http://localhost/schema" targetNamespace="http://localhost/wsdl/Services.wsdl" name="Services">
	<types>
		<xsd:schema targetNamespace="http://localhost/schema" xmlns="http://www.w3.org/2001/XMLSchema">
			<xsd:complexType name="listTablesDetails">
				<xsd:sequence>
					<xsd:element name="database" type="xsd:string" minOccurs="1" maxOccurs="1"/>
					<xsd:element name="tablename" type="xsd:string" minOccurs="1" maxOccurs="1"/>
				</xsd:sequence>
			</xsd:complexType>
		</xsd:schema>
	</types>
	<message name="listTablesRequest">
		<part name="listTablesDetails" type="xsd1:listTablesDetails"/>
	</message>
	<message name="listTablesResponse">
		<part name="response" type="xsd:string"/>
	</message>
	<portType name="PortType">
		<operation name="listTables">
			<input message="tns:listTablesRequest"/>
			<output message="tns:listTablesResponse"/>
		</operation>
	</portType>
	<binding name="Binding" type="tns:PortType">
		<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
		<operation name="listTables">
			<soap:operation soapAction="Services#listTables"/>
			<input>
				<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="Services"/>
			</input>
			<output>
				<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="Services"/>
			</output>
		</operation>
	</binding>
	<service name="Service">
		<port name="Port" binding="tns:Binding">
			<soap:address location="http://localhost:8080/soap/test.pl"/>
		</port>
	</service>
</definitions>

2 Comments

  • boring says:

    Hi,

    I have a wsdl and my client script are as follow:

    my $client = SOAP::Lite->new();

    $client = $client->service('http://mysite.com/name.wsdl');

    $result = $client->listTables('parameters');

    However, I got an error message:
    Unrecognized method ‘listTables’. List of available method(s):

    Please advice and help.

    Thanks.

  • admin says:

    The problem seems to lie with your server or wsdl rather than client. Using my server and wsdl example, the following seems to work;

    my $client = SOAP::Lite->new();
    $client = $client->service('http://myhost/test.wsdl');
    my $result = $client->listTables('listTablesDetails'=>{ database => 'information%', tablename => 'column%' });
    die Dumper($result);

    I would suggest turning on tracing using
    use SOAP::Lite +trace=>”all”;
    and reviewing the contents of your XML envelope.

    Also make sure that the wsdl soap:Action matches you server’s package and subroutine “dispatch_to” name.

    Good luck

Leave a Reply

XHTML: You can use these tags:' <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Copyright © 2008-2017 Brinsmead Data Services All rights reserved.