ptum has asked for the wisdom of the Perl Monks concerning the following question:

I'm pretty new to SOAP. I've been using SOAP::Lite to generate SOAP messages for a vendor-provided service inside our corporate firewall. For messages with simple types, I've gotten it to work, although it has involved a lot of trial and error before I got the XML formatted the way the service was expecting it.

Now I'm running into trouble generating a SOAP message with a complex type (basically an array of min and max integer values which represent ranges of numbers). The fault codes I'm getting back when my message fails are pretty useless: soap:Server in the fault code, a bunch of useless (to me, anyway) java error messages in the stacktrace, and a 500 internal server error in the SOAP::Transport response. I asked the vendor to write their service so it can accept whatever and respond in a more helpful manner (telling me if my XML was malformed, specifically showing which element had problems, etc.). They respond:

"The arguments which are passed from the SOAP client are first decoded before reaching the application. The format and data types need to match what is expected in the wdsl file."

Is there a way for me to prevalidate my XML against the WSDL? I see (or interpret, anyway) that SOAP::Lite doesn't support WSDLs with complex types ... is there another way to skin this cat? It seems like a cop-out for the vendor to defer all error handling to the WSDL if there is no way for me to verify my XML against the WSDL ... or am I misunderstanding the purpose of the WSDL?

Just for context, I'm creating my SOAP connection in this manner (I've left out some of the proprietary stuff), and executing the 'add' method as follows:

sub build_SOAP_connection { my $self = shift; my $ns = $self->{'data'}->{'namespace'}; my $ep = $self->{'data'}->{'proxy'}; my $lite = SOAP::Lite->uri($ns)->proxy($ep)->on_action( sub { join " +", @_ } ); $lite->autotype(0); $lite->soapversion('1.1'); $lite->serializer()->namespaces({ 'http://schemas.xmlsoap.org/soap/envelope/' => 'soap', 'http://schemas.xmlsoap.org/soap/envelope/' => 'soapenv', 'http://schemas.xmlsoap.org/soap/encoding/' => 'soapenc', 'http://www.w3.org/2001/XMLSchema' => 'xsd', 'http://www.w3.org/2001/XMLSchema-instance' => 'xsi' }); $self->{'SOAP'} = $lite; return $lite; } sub SOAP_execute { my $self = shift; my $method = shift; my $datagram = shift; my $SOAP; if (exists($self->{'SOAP'})) { $SOAP = $self->{'SOAP'}; } else { $SOAP = $self->build_SOAP_connection(); } my $doc = SOAP::Data->name($method)->uri($self->{'data'}->{'namespac +e'})->prefix(''); my $response; eval { $response = $SOAP->call($doc => $datagram); }; if ($@) { print "Error: $method SOAP call died: $@.\n"; return 1; } if ($response->fault) { print "Fault code: ", $response->faultcode, "\n"; print "Fault string: ", $response->faultstring, "\n"; print "Fault actor: ", $response->faultactor, "\n"; return $response->faultcode; } else { my $body = $response->result; # returns the first object unless ($body == 0) { $self->{'data'}->{'ERROR'} = "ERROR: method $method failed: $bod +y"; } return $body; } }

Should I push back to the vendor and require them to respond with decent error messages if my XML is badly formed or typed, or is their position reasonable and I simply need to figure out how to validate my own messages? Advice on validating my XML against the WSDL is particularly welcome.


No good deed goes unpunished. -- (attributed to) Oscar Wilde

2006-04-08 Retitled by planetscape, as per Monastery guidelines
Original title: 'OT: Validating SOAP with a WSDL'

Replies are listed 'Best First'.
Re: Validating SOAP with a WSDL
by idsfa (Vicar) on Apr 06, 2006 at 18:32 UTC

    On Topic

    SOAP::WSDL appears to have been designed for this problem. Soap::Lite's POD says that it doesn't fully support WSDL. Something like ...

    . . . my $soap=SOAP::WSDL->new(); $soap->wsdl($self->{'data'}->{'namespace'}); $soap->proxy($self->{'data'}->{'proxy'} ); $soap->wsdlinit; # This part is the big rebuild, as you no longer have # to hand-roll your XML ... good thing, too my $som=$soap->call( $method , name => 'value' , name => 'value' );

    The intelligent reader will judge for himself. Without examining the facts fully and fairly, there is no way of knowing whether vox populi is really vox dei, or merely vox asinorum. — Cyrus H. Gordon

      Thank you for this recommendation. SOAP::WSDL does, in fact, seem to have been designed for this problem.

      I was able to get the simple methods to work using the WSDL, which saves some grief in terms of rolling my own XML. Unfortunately, again, I got hung up on the complex types, and the error messages produced aren't very elaborate. It seems that when I call a method that involves complex types, the WSDL is not able to resolve the definitions. I suspect this is because of a corporate firewall, but I'm not sure.

      Here's the code I have so far (somewhat sanitized):

      #!/usr/local/bin/perl use strict; use warnings; use SOAP::WSDL +trace => 'all'; use Data::Dumper; Main: { my $soap=SOAP::WSDL->new( wsdl => 'http://some_ip/wsdl/myServiceName +.wsdl' ); $soap->proxy('http://some_ip:port/some/path/myServiceLocation'); $soap->servicename('myServiceName'); $soap->wsdlinit( caching => 1); my $response; eval { # this one worked with no troubles $response = $soap->call( 'queryItem' , id => 'blah' , pwd => 'bleh', itemId => '9015'); }; if ($@) { print "SOAP call failed: $@\n"; exit; } eval { # this one fails with the "error processing WSDL" message $response = SOAP_add_Item($soap); }; if ($@) { print "SOAP call failed: $@\n"; exit; } if ($response->fault) { print Dumper($response->faultdetail), "\n"; } else { my $body = $response->result; unless ($body == 0) { print "SOAP Message: ", Dumper($body); } } } sub SOAP_add_Item { my $soap = shift; my $response = $soap->call('addItem', 'id' => 'blah', 'pwd' => 'bleh', 'itemId' => '1234', 'itemName' => 'Test Item', 'Ranges' => [ { 'min' => '8605118310', 'max' => '8605118319' } ] ); return $response; } # 'Ranges' => [ # bless( { # 'min' => '8605118310', # 'max' => '8605118319' # }, 'SoapTypeRange' ) ] );

      I've tried the complex 'Ranges' type with and without the reference to the type 'SoapTypeRange', but in neither case does it seem to be able to resolve the type.

      Instead, I get this message:

      SOAP call failed: Error processing WSDL: './/xsd:element' not found at + /usr/local/lib/perl5/site_perl/5.8.6/SOAP/WSDL.pm line 504.

      That line in WSDL.pm seems to be in the context of attempting to resolve definitions for complex types. The relevant lines in the WSDL seem to be:

      <wsdl:definitions name="myServiceName" targetNamespace="http://some_ip/wsdl/myServiceName/" xmlns:tns="http://some_ip/wsdl/myServiceName/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soap12="http: +//schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:mime="http:// +schemas.xmlsoap.org/wsdl/mime/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl=" +http://schemas.xmlsoap.org/wsdl/" <wsdl:types> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://some_ip/package/various.other.soap.types /"> <xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/" /> <xsd:complexType name="SoapTypeRange"> <xsd:all> <xsd:element name="min" type="xsd:long" /> <xsd:element name="max" type="xsd:long" /> </xsd:all> </xsd:complexType> <xsd:complexType name="ArrayOfSoapTypeRange"> <xsd:complexContent> <xsd:restriction base="soapenc:Array"> <xsd:attribute ref="soapenc:arrayType" wsdl:arrayType="n11:SoapTypeRan +ge[]" /> </xsd:restriction> </xsd:complexContent> </xsd:complexType> </xsd:schema> </wsdl:types>

      I'm stumped. If I can't get outside the firewall to resolve the complex type definitions, then how can I build a workable SOAP call to this service? Any additional thoughts are very welcome.


      No good deed goes unpunished. -- (attributed to) Oscar Wilde

        One of the tools available on the soapclient.com site may be able to help in debugging. I've only used the Generic SOAP Client in the past, but it proved very handy indeed for pointing out that my initial code was a steaming pile of... ;-)

        Hope that helps.

Re: Validating SOAP with a WSDL
by jhourcle (Prior) on Apr 06, 2006 at 18:53 UTC
    Should I push back to the vendor and require them to respond with decent error messages if my XML is badly formed or typed, or is their position reasonable and I simply need to figure out how to validate my own messages?

    Most of the people who write Web Services have no idea about interoperability -- they're using toolkits to generate the service, and couldn't change the error messages if they even wanted to.

    Odds are, the stack traces they've giving back, besides being a potential security problem, actually have some clue as to where the problem is ... but you didn't have that, so I have no idea. If you can find out what SOAP toolkit they're using, you might be able to find some sort of support forum for it, and find some hints as to what the problem is.

    As for pre-validating the XML -- the bulk of WSDL is just XML Schema, so you might be able to use an XML Schema validator for the payload.

    Dealing with the 'impedence errors' that come from different SOAP toolkits is a royal pain -- make sure you're casting each element as a SOAP::Data object, or use a custom serializer -- the default SOAP::Lite serializer makes a lot of assumptions that can be false (eg, '1234' is an integer ... even though it's supposed to be a string in the WSDL).