I am trying to communicate with a 3-rd party API. I tried both savon and Soap UI but both failed, then I asked them and they gave me equivalent that works. Confused, I toggled the logger and got following XML request which is quite different from what Savon is sending.
Here is the PHP code (I guess that's not needed, but still)
$apiauth =array('UserName'=>'XXXXXX','Password'=>'XXXXXXX','ClientCode'=>'1234')
$wsdl = 'http://stagewebapi.mylescars.com/service.asmx?wsdl';
$soap = new SoapClient($wsdl);
$header = new SoapHeader('http://tempuri.org/', 'AuthHeader', $apiauth);
$soap->__setSoapHeaders($header);
$data = $soap->fetchCities($header);
print_r($data);
Here is the XML request that PHP is sending and which is working just fine
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://tempuri.org/"><SOAP-ENV:Header><ns1:AuthHeader><ns1:UserName>XXXXXXX</ns1:UserName><ns1:Password>XXXXXXX</ns1:Password><ns1:ClientCode>1234</ns1:ClientCode></ns1:AuthHeader></SOAP-ENV:Header><SOAP-ENV:Body><ns1:fetchCities/></SOAP-ENV:Body></SOAP-ENV:Envelope>
Here is my ruby code so far
require 'savon'
auth_header = { 'UserName'=>'XXXXXXX','Password'=>'XXXXXXXX','ClientCode'=>'1234'}
client = Savon.client(:wsdl => "http://stagewebapi.mylescars.com/service.asmx?wsdl",
:namespace_identifier => :ns1 ,:namespace => "http://tempuri.org/" , :log => true, :soap_header => auth_header,
:pretty_print_xml => true, :env_namespace => 'SOAP-ENV')
puts client.call(:fetch_cities)
Here is what Savon is producing
<?xml version="1.0" encoding="UTF-8"?>
<soap-env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns1="http://tempuri.org/" xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Header>
<UserName>XXXXXXXXXXXXXXX</UserName>
<Password>XXXXXXXXXXXXX</Password>
<ClientCode>2873</ClientCode>
</soap-env:Header>
<soap-env:Body>
<ns1:fetchCities/>
</soap-env:Body>
</soap-env:Envelope>
I don't know how to make it equivalent to the PHP one. Am I missing something obvious ?
I tried to look at Savon documentation to find if there are some methods to manipulate tags with fine grain control but couldn't find much about it.
Questions similar to this are either unanswered or are specific to the requests.
Some SO questions that helped a little
Convert this xml request to a proper savon request
Replicating xml requests with savon ruby
Little curious, how PHP gets it right most of the time. Is it the parser they use for wsdl?
Related
Can anyone help point me in the correct direction to generate valid XML to use with the Salesforce API. I normally avoid SOAP like the plague but have no choice here and it seems like I'm at the bottom of a vertical learning curve.
I've got a full WSDL which as far as I'm aware should cover every part of the request, including all the appropriate namespaces and complex types, but I just can't generate a request that matches their example.
A subset of the example request looks like the following -
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://soap.sforce.com/schemas/class/WebsiteAPI" xmlns:web1="http://soap.sforce.com/schemas/class/WebsiteAPIUtils">
<soapenv:Header>
<web:SessionHeader>
<web:sessionId> SESSION_ID_FROM_LOGIN_METHOD </web:sessionId>
</web:SessionHeader>
</soapenv:Header>
<soapenv:Body>
<web:upsertLeadOrUpdateAccount>
<web:leadFromWebsite>
<!--Optional:-->
<web1:salesforceId></web1:salesforceId>
...
By calling the relevant method I can get an <ns1:upsertLeadOrUpdateAccount> request (not the same namespace id but that doesn't really matter), with an empty <leadFromWebsite> element. However, I just can't work out how to get the <salesforceId> sub element in there with the correct namespace. Everything I do either seems to do nothing, or adds an "xsi-type" attribute to the parent rather than using the namespace prefix.
The closest I've got is the following, which adds the namespace to the root element, but seems to create a weird looking xsi-type="ns1:web1" attribute, rather than just prefixing all the elements with web1.
I do have complexTypes for everything in the WSDL so I'm sure there should be a way for the SOAP library to handle all the heavy lifting for me. I really don't want to resort to just generating the whole lot by hand.
$data = new stdclass();
$data->salesforceId = '123';
$args = [
'leadFromWebsite' => new SoapVar($data, SOAP_ENC_OBJECT, 'web1', 'http://soap.sforce.com/schemas/class/WebsiteAPIUtils')
];
Output -
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://soap.sforce.com/schemas/class/WebsiteAPIUtils"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ns2="http://soap.sforce.com/schemas/class/WebsiteAPI">
<SOAP-ENV:Header>
<ns2:SessionHeader>
<ns2:sessionId>123</ns2:sessionId>
</ns2:SessionHeader>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns2:upsertLeadOrUpdateAccount>
<ns2:leadFromWebsite xsi:type="ns1:web1">
<salesforceId>123</salesforceId>
</ns2:leadFromWebsite>
</ns2:upsertLeadOrUpdateAccount>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I haven't tested this fully with the salesforce API yet, but I think I've managed to generate XML that looks correct - at least a lot closer than I was.
Seems my main issue was confusing type and node namespaces. I wanted to specify the namespace for the node, not the data type.
The following code generates xml that is much closer to the example requests -
$data = [];
$data[] = new SoapVar('123', XSD_STRING, null, null, 'salesforceId', 'http://soap.sforce.com/schemas/class/WebsiteAPIUtils');
$args = [
'leadFromWebsite' => new SoapVar($data, SOAP_ENC_OBJECT, null, null, 'leadFromWebsite', 'http://soap.sforce.com/schemas/class/WebsiteAPI')
];
$s->upsertLeadOrUpdateAccount($args);
XML -
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://soap.sforce.com/schemas/class/WebsiteAPIUtils"
xmlns:ns2="http://soap.sforce.com/schemas/class/WebsiteAPI">
<SOAP-ENV:Header>
<ns2:SessionHeader>
<ns2:sessionId>123</ns2:sessionId>
</ns2:SessionHeader>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns2:upsertLeadOrUpdateAccount>
<ns2:leadFromWebsite>
<ns1:salesforceId>123</ns1:salesforceId>
</ns2:leadFromWebsite>
</ns2:upsertLeadOrUpdateAccount>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I need this xml in out:
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="https://server/">
<soapenv:Header>
<authenticate>111111111111</authenticate>
<SOAP-ENV:Header/>
My php code is:
$head = new stdClass();
$head->authenticate='1111111111111111';
$header = new SoapHeader('ns1','authenticate',$head,false);
$client = new SoapClient ('https://server',array("trace" => 1, "exceptions" => 1));
$client->__setSoapHeaders($header);
I get this XML:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="https://server"
xmlns:ns2="https://server">
<SOAP-ENV:Header>
<ns2:authenticate>
<authenticate><BOGUS>anc401t9n4dknmgp2bv629ori7</BOGUS>
</authenticate></ns2:authenticate>
</SOAP-ENV:Header
><SOAP-ENV:Body>...
How can I get needed xml? Can fully rewrite soapheader with php client SoapClient? Or how I can adding one string whithout creating new namespace?
If your Web Service is well documented within its WSDL, try using wsdltophp.com or PackageGenerator as it generates the methods to properly set the SoapHeader very easily and so for the whole request.
I am trying to build a web service to a specification of a 3rd party who will be connecting to it as a client, the service must be written in PHP but I am a total PHP n00b so struggling a little. Basically my responses are being wrapped in a element however the client is not accepting my responses because of this, here is an example.
service.php
<?php
class MyService {
public function Ping() {
return date('d/m/Y H:i:s');
}
}
$server = new SoapServer(null, array( 'soap_version' => SOAP_1_2, 'encoding' => 'UTF-8', 'uri' => 'http://tempuri.org/'));
$server->setClass("MyService");
$server->handle();
?>
Request
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:tem="http://tempuri.org/">
<soapenv:Header/>
<soapenv:Body>
<tem:Ping />
</soapenv:Body>
</soapenv:Envelope>
Response
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://tempuri.org/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:PingResponse>
<return xsi:type="xsd:string">18/11/2010 18:51:02</return>
</ns1:PingResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
What the client is expecting is the PingResponse to just contain the date but without the wrapped around the outside. How can I get the PHP SoapClient to do this?
E.g.
<ns1:PingResponse>18/11/2010 18:51:02</ns1:PingResponse>
I don't think you can, I think this is written into the core of SOAP so it's something your client will have to work around. If they're already getting the contents of PingResponse I can't see it being much more difficult for them to go 1 tree deeper into the response.
The format of my SOAP call needs to be:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://tempuri.org/">
<SOAP-ENV:Body>
<ns1:FunctionName>
<ns1:HostUserName>hostuser</ns1:HostUserName>
<ns1:HostPassword>pass</ns1:HostPassword>
<ns1:OwnerName>owner</ns1:OwnerName>
<ns1:UserName>user</ns1:UserName>
<ns1:OptionalParam>
<ns1:Parameters>
<ns1:Parameter Name="FirstName">First<ns1:/Parameter>
<ns1:Parameter Name="LastName">Last<ns1:/Parameter>
<ns1:/Parameters>
</ns1:OptionalParam>
<ns1:/FunctionName>
</SOAP-ENV:Body>
How do I represent this in a PHP request?
I build the request like this:
$client = new SoapClient($url,array('trace' => 1));
$data['HostUserName']=$this->hostname;
$data['HostPassword']=$this->hostpassword;
$data['OwnerName']=$this->ownername;
$data['UserName']=$username;
$data['FirstName']=$FirstName;
$data['LastName']=$LastName;
$result = $client->FunctionName($data);
I've tried a few combinations of building arrays, SOAPVAR and SOAPPARAM but nothing has worked.
The target SOAP server is .NET based.
Any suggestions?
Thanks
I want to imitate following request using Zend_Soap_Client.
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Header>
<h3:__MethodSignature xsi:type="SOAP-ENC:methodSignature"
xmlns:h3="http://schemas.microsoft.com/clr/soap/messageProperties" SOAP-ENC:root="1"
xmlns:a2="http://schemas.microsoft.com/clr/ns/System.Collections">xsd:string a2:Hashtable</h3:__MethodSignature>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<i4:ReturnDataSet id="ref-1" xmlns:i4="http://schemas.microsoft.com/clr/nsassem/Interface.IRptSchedule/Interface">
<sProc id="ref-5">BU</sProc>
<ht href="#ref-6"/>
</i4:ReturnDataSet><br/>
<a2:Hashtable id="ref-6" xmlns:a2="http://schemas.microsoft.com/clr/ns/System.Collections">
<LoadFactor>0.72</LoadFactor>
<Version>1</Version>
<Comparer xsi:null="1"/>
<HashCodeProvider xsi:null="1"/>
<HashSize>11</HashSize>
<Keys href="#ref-7"/>
<Values href="#ref-8"/>
</a2:Hashtable>
<SOAP-ENC:Array id="ref-7" SOAP-ENC:arrayType="xsd:anyType[1]">
<item id="ref-9" xsi:type="SOAP-ENC:string">#AppName</item>
</SOAP-ENC:Array><br/>
<SOAP-ENC:Array id="ref-8" SOAP-ENC:arrayType="xsd:anyType[1]">
<item id="ref-10" xsi:type="SOAP-ENC:string">AAGENT</item>
</SOAP-ENC:Array>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
It seems somehow I've to send hashed "ref-7" and "ref-8" array embedded inside body? How can I do this?
Function ReturnDataSet takes two parameters, how can I send additional "ref-7" and "ref-8" array data?
$client = new SoapClient($wsdl_url, array('soap_version' => SOAP_1_1));
$result = $client->ReturnDataset("BU", $ht);
I don't know how to set $ht, so that hashed data is sent as different body entry.
Thanks.
have you tried sending a two-dimensional array as $ht? From looking at the code, $ht corresponds to a hashtable with ref-7 and ref-8 being an array themselves.
I tried to open this service in Visual Studio to get an idea of the type of parameters, but I recieved a few schema errors, mainly missing dataTypes. Are you sure this web service is configured and working like it should for others?