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

Hello Monks

I am working with a 3rd party API and trying to use SOAP::Lite for the first time.

I've figured out how to actually make the request but to use the secure version I essentially need to send an sha1 hash of the message body in a header.

What I've got so far:

#!/usr/bin/env perl use strict; use warnings; use 5.010; use LWP::UserAgent; use SOAP::Lite; my $client = SOAP::Lite->proxy($proxy) ->ns($namespace, 'foo') # I'm not sure I understand this. ->uri($uri) ->on_action(sub { sprintf '%s', $_[0] }) ->on_fault(sub { my($soap, $result) = @_; die ref $result ? "Fault Code: " . $result->faultcode . "\n" . "Fault String: " . $result->faultstring . "\n" : $soap->transport->status, "\n"; }); my $params = { foo => 'bar', biz => 'baz'}; my $result = $client->my_api_method($params);

Essentially, after the xml is generated and before the request is made I need to insert a sha1 hash of the request body. I'm looking at the correct version of the docs now (was looking at 0.55 for some reason...) so hopefully there's something obvious that jumps out.

EDIT: I think I can use SOAP::Data->name($params) somehow...

NOT SOLUTION:

you need to tell it what type of data you're giving it. (I think that's the right vocabulary)

my $data = SOAP::Data->name( %params); #say "Data: $data"; my $serializer = $client->serializer; my $xml = $serializer->envelope(method => 'my_api_call', $data);

now I still call $client->my_api_method(\%params); so I'm doing more work than necessary... right? does it matter? there's probably a way to feed the envelope to the SOAP::Lite->call('my_api_method' => \%params)

EDIT:

Hmm... turns out I was wrong. there are some tags that are added when the request is made. Back to the drawing board.

REAL SOLUTION

So the deal is, you need to hook into the LWP request and get the body from LWP. Anonymous Monk had basically pointed to the answer off the bat :)

#!/usr/bin/env perl use strict; use warnings; use 5.010; use LWP::UserAgent; use SOAP::Lite; my $client = SOAP::Lite->proxy($proxy) ->ns($namespace, 'foo') # I'm not sure I understand this. ->uri($uri) ->on_action(sub { sprintf '%s', $_[0] }) ->on_fault(sub { my($soap, $result) = @_; die ref $result ? "Fault Code: " . $result->faultcode . "\n" . "Fault String: " . $result->faultstring . "\n" : $soap->transport->status, "\n"; }); my $params = { foo => 'bar', biz => 'baz'}; $client->transport->add_handler("request_prepare", \&modify_header ); my $result = $client->my_api_method($params); sub modify_header { my ($request, $ua, $h) = @_; $request->header('some-special-header' => sha1 $request->content); }

Replies are listed 'Best First'.
Re: [SOAP::Lite] Obtain request body before request is sent?
by Anonymous Monk on Jan 28, 2015 at 02:38 UTC

    $soap->transport is your LWP object, so hook it regular LWP style, I got example in my SOAP FAQ

    OTOH, XML::Compile::SOAP... probably already does whatever it is you're doing ...

      so what happens when I call $client->my_api_method? A serializer is called? I think it would be ideal to just modify how the default serializer behaves rather than roll my own...

      What is "regular LWP style"?

      I'm not sure why you linked me to a perlmonks thread, but are they making their own serializer? I think that was the answer to the original question on how to compose a specific message body? That makes me nervous, I'm still not really sure I understand all that is going on with SOAP::Lite, so I'm sorry if these are really obvious questions.

      Thanks for the reply!

      EDIT: there's a $client->call($method => @arguments); that is designed to allow the program control over the call. so maybe I can "manually" do what $client->my_api_method(\%params) does.

      EDIT2: There's references in the docs to SOAP::Serializer all over the place. <code> serializer(optional serializer object) $serial = $client->serializer( ) Gets or sets the serializer object used for creating XML messages. See SOAP::Serializer for details. Gives the application access to the type-lookup table from the serializer object. See the section on SOAP::Serializer. If you want change behavior for specific instance of SOAP::Lite, you may subclass SOAP::Serializer, override as_string() method that is responsible for string encoding (take a look into as_base64Binary()) and specify new serializer class for your SOAP::Lite object with: <code>

      but the link to SOAP::Serializer https://metacpan.org/pod/SOAP::Serializer only says it's used internally by SOAP::Lite. I think that's the way to go... but no docs! lol