http://www.perlmonks.org?node_id=1067390

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

Hello. I am having some difficulty with the XML::Compile::WSDL11 module. I actually have no difficulty running a login operation and obtaining a session token:
use strict; use warnings; use XML::Compile::SOAP11; use XML::Compile::WSDL11; use XML::Compile::Transport::SOAPHTTP; my $wsdl_file = '/home/user/some_wsdl_file.wsdl'; my $wsdl = XML::Compile::WSDL11->new($wsdl_file); $wsdl->importDefinitions('/home/user/schema1.xsd'); $wsdl->importDefinitions('/home/user/schema2.xsd'); $wsdl->importDefinitions('/home/user/schema3.xsd'); my %request = ( Input => { Username => 'username', Password => 'password' } ); my $call = $wsdl->compileClient('LogIn'); my $answer = $call->(%request); my $sessionToken = $answer->{parameters}{Output}{SessionToken};
The SOAP request:
<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/env +elope/"> <SOAP-ENV:Body> <tns:LogIn xmlns:tns="http://example.com/tns" xmlns:tns1="http://example.com/tns1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <tns:Input> <tns1:Username>username</tns1:Username> <tns1:Password>password</tns1:Password> </tns:Input> </tns:LogIn> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
This successfully retrieves the session token, as seen in the SOAP response:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Header> <token xmlns="http://example.com/tns">sessiontoken</token> </s:Header> <s:Body> <LogInResponse xmlns="http://example.com/tns"> <Output xmlns:a="http://example.com/tns1" xmlns:i="http://www.w3 +.org/2001/XMLSchema-instance"> <a:SessionToken>sessiontoken</a:SessionToken> </Output> </LogInResponse> </s:Body> </s:Envelope>
The problem I am having is that every other request requires passing this token back. This token needs to be in the SOAP header block. However, I am at a loss as to how to insert anything into the header. For instance, the request to change the password would something like this:
<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/env +elope/"> <SOAP-ENV:Header> <tns2:token xmlns="http://example.com/tns">sessiontoken</v2:token> </SOAP-ENV:Header> <SOAP-ENV:Body> <tns:GetNewPassword xmlns:tns="http://example.com/tns" xmlns:tns2="http://example.com/tns2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <tns:Input> <tns2:OldPassword>password</tns2:OldPassword> </tns:Input> </tns:GetNewPassword> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
The key difference being the SOAP-ENV:Header tag. This is the portion I am unable to add. The only way I was ever able to affect the header was trying to use XML::Compile::SOAP::WSS, however this is for an authentication that the API I am trying to access does not use. Any ideas where I am going wrong? I should note there is no header information at all in the WSDL files themselves. Thanks.

Replies are listed 'Best First'.
Re: Passing a session token using XML::Compile::WSDL
by runrig (Abbot) on Dec 17, 2013 at 00:30 UTC
    I've got this as part of my SOAP calling code. Pass in the credentials under a 'Header' key:
    my $tmp = $f->( Header => $self->{CREDENTIALS}, @_ );
    The full generic method I use to compile SOAP operations is here (but no credentials used in that example).
      Thank you. This has started me on the right track, but I'm still not quite there yet. The parameter appears to be 'header' with a lowercase 'h'. But it seems like it only accepts an arrayref containing a hashref. I tried looking through the modules themselves as well as the documentation to see how to structure this and eventually I came to this:
      my $header = [{ parts => [{ name => 'token', element => 'tns:token', destination => $sessionToken, mustUnderstand => 1 }] }];
      This structure got me the farthest (I was either getting errors about it not being a hashref or arrayref, or a "panic: findName called without name" before). But now I'm getting this error:
      error: cannot find element or attribute `{http://example.com/tns}token +'
      That despite when I do the login and do a dumper on the $answer variable I get this:
      Answer: $VAR1 = { 'parameters' => { 'Output' => { 'SessionToken' => 'sessiontokenv +alue' } }, '{http://example.com/tns}token' => bless( do{\(my $o = 44800 +400)}, 'XML::LibXML::Element' ) };
      Yet when I try to add that into the header with that same namespace, it complains it can't find the element. Now I tried making a schema just for this token (since it is not defined in any of the WSDLs or XSDs that the company whose API I am trying to access provides):
      <?xml version="1.0" encoding="utf-8"?> <xs:schema elementFormDefault="qualified" targetNamespace="http://example.com/tns" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://example.com/tns"> <xs:element name="token" nillable="true" type="xs:string" /> </xs:schema>
      Now I get a different error: Can't call method "header" on unblessed reference at /usr/local/perl/5.16.1/lib/site_perl/5.16.1/XML/Compile/Transport/SOAPHTTP.pm line 340. So now it wants the header to be an object, and it appears the default value comes from HTTP::Headers (which is not the kind of header I want, I'm looking for the SOAP envelope header tag. So how do I build the header element? Thanks.