SOAP::Lite, parameter-addressing - perl

I am using SOAP::Lite to use a WSDL-defined webservice.
My request (that is working fine) is like that.
<soapenv:Envelope xmlns:soapenv="http://myabc">
<soapenv:Header/>
<soapenv:Body>
<foo>
<p1>max</p1>
<p2>frank</p2>
</foo>
....
My perl code.
my $service = SOAP::Lite->service ("http://mywsdl");
my $ret = $service->foo ("max", "frank");
That is working too.
But I like to name/address my parameters p1 and p2 to have more flexibility.
I tried it with a hash
my %params = (p1 => "max", p2 => "frank");
and also with SOAP::Data.
my #params = (
SOAP::Data->name (p1 => "max"),
SOAP::Data->name (p2 => "frank"));
But it is not working that way.
String value expected instead of SOAP::Data reference
Any ideas how to name my parameters?
EDIT
I like to use wsdl service. So how do I know how the service functions expect their parameters??
Thats the core of my question. I thought about the naming of parameters for a workaround.

If you want to specify the names of the parameters then you should avoid creating a service, which is mostly intended to avoid such housekeeping
If you simply call the method and supply its parameters then it should do what you want
$client->call(foo =>
SOAP::Data->name( p1 => 'max' ),
SOAP::Data->name( p2 => 'frank' )
);
Note that
SOAP::Data->name( p1 => 'max' )
is an undocumented contraction of
SOAP::Data->name('p1')->value('max')
or
SOAP::Data->new( name => 'p1', value => 'max' )

If you would like to use a WSDL file (welcome to hell), you need to use SOAP::WSDL instead of SOAP::Lite. Then you will have an utility called wsdl2perl.pl. If you run it on your wsdl file it will create a lot of perl code, including some classes for each service defined in the wsdl file.
Then in your code you instantiate a new instance of the specific endpoint you want to use, and then you call on this object methods named after the services offered on this endpoint (you do not use the call method anymore), passing a hash reference with all parameters.
You will still need to know what parameters are expected because there is not a lot of introspection available (you can have a workaround that because if you pass an unknown parameter name to the classes they will die and in the error message you will have the list of available parameters, so you can catch that and parse the error string. Ugly, but I did not find another way to do it).

I don't think you can name the parameters if you create the stubs from the service descriptions by calling service().
If you want to assign values to the parameters, there is an example available on CPAN.
Function sayHello
<sayHello xmlns="urn:HelloWorld">
<name xsi:type="xsd:string">Kutter</name>
<givenName xsi:type="xsd:string">Martin</givenName>
</sayHello>
SOAP request
use SOAP::Lite;
my $soap = SOAP::Lite->new( proxy => 'http://localhost:81/soap-wsdl-test/helloworld.pl');
$soap->default_ns('urn:HelloWorld');
my $som = $soap->call('sayHello',
SOAP::Data->name('name')->value('Kutter'),
SOAP::Data->name('givenName')->value('Martin')
);
die $som->faultstring if ($som->fault);
print $som->result, "\n";
You have to replace the value of proxy to http://mywsdl, name and givenName to p1 and p2, sayHello to foo and urn:HelloWorld to your WSDLs namespace.
I also recommend to check this. All the requests can be done without SOAP::Lite.

Related

How to get response from a SOAP request using zend-soap?

I've been spending sometime with problem. I have a endpoint I want to send some data and receive a response.
I've look online and I've seen that Zend\Soap\Server is used to build methods, and Zend\Soap\Client can than use those methods. I would like for someone to explain what to write in those methods, and how that helps with getting a response.
$client = new Client($this->wsdl, array('soap_version' => SOAP_1_1));
Now we can $client->SOMEMETHOD();
My questions are: 'Where do I get this method from?', 'what will method do?', and 'how do I use it?'
SOAP short base
SOAP allows to request an online service. (use as a client code) for example you can query AMAZON on a product, know its price, etc.
SOAP works in 2 different ways:
way 1: wdsl mode
when you create a connection to a SOAP client, you must provide a link that will provide an XML file: the wdsl
example: type in your browser:
http://webservices.amazon.com/AWSECommerceService/AWSECommerceService.wsdl
congratulation : you see (discover) the way to query AMAZON !
this XML file tells you what you can ask for: a price, a product info, a search, etc ..: these are the routes.
for each route (each possible query) the parameters you must provide, the validity check of these parameters: example: route = search article, param1 = article name, type of parameter = string, etc...
$client = new Client($this->wsdl, array( 'soap_version' => SOAP_1_1 ) )
create a client object :
$this->wsdl a link to xml file (the discovery part)
it's a URI string : example : "http://webservices.amazon.com/AWSECommerceService/AWSECommerceService.wsdl"
array( 'soap_version' => SOAP_1_1 ) = i use SOAP version xx, you can add more options in this array.
way 2: non wdsl mode
you do not provide a wsdl link or file...
but you must know how to handle request and responses
deep learning
search on google a tutorial for SOAP,
there are online requester for test purpose, etc...
then use it in zend
I solved my problem, so I'll post it here for anyone to understand.
$client = new Client($wsdl, ['soap_version' => SOAP_1_1]);
$params = [
'args0' => [
'_PRCODASSOC' => null,
'_PRCODDELEG' => null,
'_PRCODFISCALE' => 'BRSLSN312213TY',
'_PRCODFSDDIRI' => null,
'_PRTIPOOPERAWS' => 'REPFAM'
]
];
$client->ws_fam_sgf($params);
$result = $client->getLastResponse();
die($result);
All I did was add 'args' => [] and added all my parameters inside that key.

Getting "A service address has not been specified..." when using Perl SOAP::Lite to do call using wsdl

I'm trying to do a SOAP call to http://www.webservicex.net/ConvertSpeed.asmx using the WSDL.
My code is as follows
use SOAP::Lite;
my $client = SOAP::Lite->new;
$client = $client->service( "http://www.webservicex.net/ConvertSpeed.asmx?wsdl" );
my $soapResp = $client->call( "ConvertSpeed", 100, "kilometersPerhour", "milesPerhour" );
if ( $soapResp->fault ) {
print $soapResp->faultstring;
}
else {
print $soapResp->result;
}
This gives me the following error
A service address has not been specified either by using SOAP::Lite->proxy() or a service description)
I believe that the WSDL is supposed to provide the service address, but it does not seem to be doing that here.
I have tried setting the service address to http://www.webservicex.net/ConvertSpeed.asmx via $client->proxy("http://www.webservicex.net/ConvertSpeed.asmx") after $client->service(), but that just gives me an error saying:
Server did not recognize the value of HTTP header SOAPAction: #ConvertSpeed
I assumed the WSDL was supposed to provide the service address and so omitted the $client->proxy() bit and tried other stuff.
This ranged from chaining methods differently and eventually chaining as little as possible (the code above) to changing the way the call is made.
From this answer I tried
$client->ConvertSpeed(100, "kilometersPerhour", "milesPerhour")
which just seems to return nothing.
This is running on Perl 5.10 and version 0.714 of SOAP::Lite.
I'm not sure what else to try, but I have confirmed that the same SOAP call works from Python(see edit).
EDIT:
from zeep import Client
client = Client('http://www.webservicex.net/ConvertSpeed.asmx?wsdl')
result = client.service.ConvertSpeed(100, 'kilometersPerhour', 'milesPerhour')
print(result)
I posted this question to PerlMonks as well since it didn't seem to be doing well here. According to the answer I received there SOAP::Lite isn't ideal for a complex service using the WSDL. Instead everything should be specified using the uri() and proxy() methods. If the WSDL is to be used then XML::Compile::WSDL11 is the module to look at.
The answer supplies working code for the call using SOAP::Lite without the WSDL as follows :
$client = SOAP::Lite->new(
uri => 'http://www.webserviceX.NET/',
proxy => 'http://www.webservicex.net/ConvertSpeed.asmx',
);
$client->on_action(sub {"http://www.webserviceX.NET/ConvertSpeed"});
$client->autotype(0);
$sheader = SOAP::Header->new('');
$sheader->type(xml => '');
$client->ns('http://www.webserviceX.NET/','web');
$client->envprefix('soapenv');
push #request, SOAP::Data->name(speed => 100)->prefix('web');
push #request, SOAP::Data->name(FromUnit => 'kilometersPerhour')->prefix('web');
push #request, SOAP::Data->name(ToUnit => 'milesPerhour')->prefix('web');
$soapResp = $client->ConvertSpeed($sheader,#request);
print $soapResp->result,"\n";
If I get a working example using XML::Compile::WSDL11 I will post it here. As I am unsure if I am going to look into it soon I decided to at least post this since the question is more focused on using a WSDL with SOAP::Lite

What is the purpose of a route name in mojolicious?

I have been learning how to program apps using the Mojolicious framework and I am stumped as to why you use route names. For example a route could say
$r->route('/cities/new')
->via('get')
->to(controller => 'cities', action => 'new_form')
->name('cities_new_form');
But what is the purpose of the name parameter? I am new to web frameworks, so maybe this has a trivial answer to it.
Naming the route allows you to reference it later if you want to generate a URL dynamically. With your example, you could do this later in your code:
my $link = $self->url_for( 'cities_new_form' )
and $link would automatically be populated with a URL ending in /cities/new. You can get fancy if your route has dynamic parts. For example:
$r->route( '/cities/:cityname' )
->via( 'get' )
->to( controller => 'cities', action => 'new_form' )
->name( 'cities_new_form' );
Then you can generate a URL like
my $link = $self->url_for( 'cities_new_form', cityname => 'newyork' );
And $link would end up with /cities/newyork.
These are trivial examples, but you can build up fairly complex stuff once your routes get more involved.
If you don't name the route, it gets a default name which is just a concatenation of the alphanumeric characters in it. That can get tedious for long routes so you can use names to abbreviate them.
See also Named Routes in the Mojolicious documentation.

Perl SOAP::Lite and Service Description to Request Object

I'm using Perl's SOAP::Lite to access a remote web service defined by a WSDL. That means I have:
use strict;
use Data::Dumper;
use SOAP::Lite +trace => 'debug';
my $service = SOAP::Lite->service('http://path/wsdl');
Ok so far. Problem is that I need access to the HTTP::Request object to send along custom HTTP request headers (and I'm not talking about authentication headers). It looks like I can access the request object after doing a successful call:
my $result = $service->getClient('parameters');
print Dumper($service->transport->http_request);
That'll give me the correct HTTP::Request object:
$VAR1 = bless( {
'_content' => '',
'_uri' => undef,
'_headers' => bless( {}, 'HTTP::Headers' ),
'_method' => undef
}, 'HTTP::Request' );
If I try to access the request object before doing an autoDispatch (the $service->getClient part), the transport object is empty and I have no way of modifying the request. It seems like everything would work fine if I were going the SOAP::Lite->proxy way -- but that defeats the helpfulness of having a pre-defined service definition.
Any ideas how I'm suppose to access the request object from a service definition without first having to make a call? Chicken and egg problem really...
Thanks!
What I'm trying to accomplish is populating the transport before doing a service call.
And you do exactly that by adding the appropriate handler, because the transport is not empty
Add a handler to the transport, see LWP::Debug for example, see LWP::UserAgent for documentation, or perlmonks.org/?node_id=904166 for example

Magento soap v2 error: Attribute "available_sort_by" is required

I'm trying to create category with magento soap v2 api call. I'm getting the error:
Attribute "available_sort_by" is required.
Code for calling the Api:
$category_data = array( "name" => "testcategory", "is_active" => "1", "include_in_menu" => "1","available_sort_by" => "","default_sort_by" => "");
$result = $client->catalogCategoryCreate($session,2,$category_data,1);
echo $result;
I have tried also with "available_sort_by" => array("name", "price", ...)
Is this the right way for calling the v2 soap api.
Thanks for any advice.
The WSDL does define what data you need for your call and exposes it for you to look at if you put 'trace' on your API SOAP calls.
After your call, with trace on, you can get your last XML, but it will be tidied up by the SOAP to be shoehorned into what the WSDL thinks you need for that call. The data will be different to what you submitted and show fields you never knew were in existence - it kind of reveals the documentation that you wish you had to start with.