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

Hi fellow monks,

I'm writing a soap server using Apache::SOAP (part of SOAP::Lite).
The SOAP server provides an interface to a method DomainCheck->Check($domain) which performs various checks on a domain name. Check() returns an anon hash: { error => $int, severity => $int, text => $string }

It works great with a SOAP::Lite client but .Net clients are confused by the xml sent back by the server.

Here's my Apache::SOAP config in httpd.conf:

<Location /DomainCheck> SetHandler perl-script PerlHandler Apache::SOAP PerlSetVar dispatch_to "DomainCheck" </Location>
A simple perl client:
#!/usr/bin/perl use strict; use SOAP::Lite; my $proxy = "https://10.191.110.64:8443/DomainCheck"; my $uri = "http://10.191.110.64/DomainCheck"; my $soap = SOAP::Lite->uri($uri)->proxy($proxy); $soap->on_debug(sub{print@_}); my $result = $soap->Check("perl.com");
Here's the xml sent by the perl client:
<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/1999/XMLSchema-instanc +e" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:S +OAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http:/ +/www.w3.org/1999/XMLSchema" SOAP-ENV:encodingStyle="http://schemas.xm +lsoap.org/soap/encoding/"> <SOAP-ENV:Body> <namesp1:Check xmlns:namesp1="http://10.191.110.64/DomainCheck"> <c-gensym3 xsi:type="xsd:string">perl.com</c-gensym3> </namesp1:Check> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
and the xml sent back by the server:
<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/1999/XMLSchema-instanc +e" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:S +OAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http:/ +/www.w3.org/1999/XMLSchema" SOAP-ENV:encodingStyle="http://schemas.xm +lsoap.org/soap/encoding/"> <SOAP-ENV:Body> <namesp7:CheckResponse xmlns:namesp7="http://10.191.110.64/DomainC +heck"> <s-gensym27> <text xsi:type="xsd:string">Valid Domain</text> <error xsi:type="xsd:int">0</error> <severity xsi:type="xsd:int">0</severity> </s-gensym27> </namesp7:CheckResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
What confuses the .net client is mainly the <s-gensym27> tag and the <namesp7> namespace. I have no idea where these tags come from, and why the number after them seem random (they change after each call)

Anyhow, here's what a .net client would expect:

<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <CheckResponse xmlns="http://snoogen/DomainCheck"> <severity>0</severity> <error>0</error> <text>ok</text> </CheckResponse> </soap:Body> </soap:Envelope>
Is there a way to make Apache::SOAP generate .net-friendly SOAP messages?

Cheers,
theblop.

Replies are listed 'Best First'.
Re: .net-friendly soap messages with Apache::SOAP
by jhourcle (Prior) on Apr 08, 2005 at 17:54 UTC

    The mysterious element you're seeing is because you're returning an anonymous element of some sort. You can get around this by either writing a custom serializer or returning SOAP::Data objects. There was an article on Majordojo about this. (I highly suggest reading all of the SOAP::Lite articles on there, if you're going to be spending much time with the module. (note -- SOAP::Serializer as distributed in CPAN is different from the SOAP::Serializer package within in SOAP::Lite; look at SOAP::Lite for SOAP::Data, as well)

    Also, based on the format you described, you're looking more for wrapped document/literal as opposed to SOAP::Lite's default, which is RPC/encoded. (here's an article that explains the differences). There's going to be options in SOAP::Lite to supress the automatic namespaces, but I don't know if there's a way to supress the type attribute built in yet. (I haven't been keeping up with the latest betas, I just went to look at the new site design that was mentioned today on the mailing list) You can always override the serializer to do it, though.

      I've been trying to create a custom Serializer for my soap server, but it looks rather complicated to find a generic solution that would work with any kind of data.

      Here's my (very) custom serializer:

      package MySerializer; @MySerializer::ISA = 'SOAP::Serializer'; sub envelope { my $self = shift; my $type = shift; if ($type =~ /^response$/ && $_[0] eq 'CheckResponse') { $_[0] = SOAP::Data->name($_[0])->prefix(''); $_[1] = SOAP::Data->value( SOAP::Data->name(text => $_[1]->{text}), SOAP::Data->name(severity => $_[1]->{severity}), SOAP::Data->name(error => $_[1]->{error}), ); } $self->SUPER::envelope($type, @_); }
      And the resulting response XML:
      <?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/1999/XMLSchema-instanc +e" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:S +OAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http:/ +/www.w3.org/1999/XMLSchema" SOAP-ENV:encodingStyle="http://schemas.xm +lsoap.org/soap/encoding/"> <SOAP-ENV:Body> <CheckResponse> <text xsi:type="xsd:string">Valid Domain</text> <severity xsi:type="xsd:int">0</severity> <error xsi:type="xsd:int">0</error> </CheckResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
      Which is quite close to what the .net client expects.

      The only problem with that solution is that the serializer has to be modified if the Check() method changes, and any other method exposed via SOAP::Lite will need a custom serializer to achieve the same compatibility.

      Is it possible to write the serializer in a more generic way? I haven't found anything in all the SOAP::Lite articles I read on the web, nor in the code for SOAP::Serializer (which I found rather hard to follow!)

      Cheers,
      theblop.