php consume WCF SSL SOAP Client 1.2 - php

Im having some problems consuming a wsHttpBinding WCF from PHP. I originally tried to use SOAP1.2 but couldnt get it to specify the WS Action.
I downloaded the nusoap library. I was originally getting an error saying that the webservice wouldnt accept the data due to a type mismatch (text/xml instead of the expected application/soap+xml). I managed to make changes to nusoap.php to send the data as application/soap+xml). Now that doesnt throw an error, i get 400 bad request error from the server.
I can consume the service from WCFTestClient and also from SOAPUI without any messing around, but just cannot get it to fly from PHP. I even copied the entire soap envelope from SOAPUI and set the $soapmsg in nusoap.php to be exactly that and it still fails.
So anyone want to offer some guidance.
EDIT
This is the code i was trying in SOAP 1.2
$params = array("soap_version"=> SOAP_1_2,
"trace"=>1,
"exceptions"=>0,
);
$client = #new SoapClient('https://localhost/wcftest/Service.svc?wsdl',$params);
$retval = $client->GetData(array('value'=>'stuff'));
if (is_soap_fault($retval)) {
trigger_error("SOAP Fault: (faultcode: {$retval->faultcode}, faultstring: {$retval->faultstring})", E_USER_ERROR);
}
EDIT #2
This is the code that works out of SOAPUI
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:tem="http://tempuri.org/">
<soap:Header xmlns:wsa="http://www.w3.org/2005/08/addressing"><wsa:Action>http://tempuri.org/IService/GetData</wsa:Action></soap:Header>
<soap:Body>
<tem:GetData>
<!--Optional:-->
<tem:value>stuff</tem:value>
</tem:GetData>
</soap:Body>
</soap:Envelope>
After adding the SoapHeaders manually as mentioned by Gords link below i get this as the __last_request when debugging with netbeans and still the same error
"<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://tempuri.org/">
<env:Header>
<ns1:Action>http://tempuri.org/IService/GetData</ns1:Action>
</env:Header>
<env:Body><ns1:GetData><ns1:value>stuff</ns1:value></ns1:GetData></env:Body></env:Envelope>
any advice??
Thanks!
Andy

Ok so i got this to work. Thanks to Gord for making me double check stuff that i had overlooked earlier on.
I ended ditching nusoap and just sticking with with SOAP 1.2. Here is my php code
//Declare some paramaters for our soapclient. Need to make sure its set to soap 1.2
$params = array("soap_version"=> SOAP_1_2,
"trace"=>1,
"exceptions"=>0,
);
//Create the soap client
$client = new SoapClient('https://localhost/wcftest/Service.svc?wsdl',$params);
//add some WSAddressing Headers in. Ensure that you have the Namespace as the address if you are using wsHttpBinding on the endpoint
//This was the step that took me the longest to figure out!
$actionHeader = new SoapHeader('http://www.w3.org/2005/08/addressing','Action','http://tempuri.org/IService/GetData',true);
//Add the headers into the client
$client->__setSoapHeaders($actionHeader);
//Make the call and pass in the variables that we require to go to the server
$retval = $client->__soapCall('GetData',array('value'=>'stuff'));
//Some Error Catching
if (is_soap_fault($retval)) {
trigger_error("SOAP Fault: (faultcode: {$retval->faultcode}, faultstring: {$retval->faultstring})", E_USER_ERROR);
}
and the working envelope
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://tempuri.org/" xmlns:ns2="http://www.w3.org/2005/08/addressing">
<env:Header>
<ns2:Action env:mustUnderstand="true">http://tempuri.org/IService/GetData</ns2:Action>
</env:Header>
<env:Body>
<ns1:GetData/>
</env:Body>
</env:Envelope>
and the web.config file from the WCF Service
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
</system.web>
<connectionStrings>
<add name="Timeforce" connectionString="Data Source=apps.ziptrek.com;Initial Catalog=qqest;User ID=qqest; Password=qqest;"
providerName="System.Data.SqlClient" />
</connectionStrings>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="TransportSecurity">
<security mode="Transport">
<transport clientCredentialType="None"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="Service" behaviorConfiguration="Service1">
<endpoint address="https://localhost/wcftest/Service.svc" binding="wsHttpBinding" bindingConfiguration="TransportSecurity" contract="IService"></endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="Service1">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpsGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="httpsService1">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="false"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>

Related

php SOAP request gives internal server error with specific method

im trying to build a soap request for the following wsdl method= "GetData"
note that not all the values are required, i've been struggling with this for a week, any help would be very appreciated
Host: xxxxxxxxxxxxxxxx
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "xxxxxxxxxxx"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetData xmlns="xxxxxx">
<key>string</key>
<transferDef>
<Where />
<OrderBy>
<OrderByItems>
<BinaryArithmetic xsi:nil="true" />
<Categorization xsi:nil="true" />
<Function xsi:nil="true" />
<QueryField xsi:nil="true" />
<QueryForm xsi:nil="true" />
</OrderByItems>
</OrderBy>
<ProjectId>string</ProjectId>
<DbType>Production or Test</DbType>
</transferDef>
<token>
<Id>guid</Id>
<LastResponseIdReturned>long</LastResponseIdReturned>
<FirstResponseIdReturned>long</FirstResponseIdReturned>
<NumberOfResponsesReturned>long</NumberOfResponsesReturned>
<DatasetsReturned>long</DatasetsReturned>
<LastDataSet>boolean</LastDataSet>
<ProjectId>string</ProjectId>
<ChangeTrackingVersion>long</ChangeTrackingVersion>
</token>
</GetData>
</soap:Body>
</soap:Envelope>
Nowadays, the best way to consume SOAP WS in PHP is to use a WSDL to PHP such as the PackageGenerator project which will generate the classes to construct the request, then to send the request then to handle the response all that using PHP objects. The generated classes are sufficiently intuitive to construct the request. In addition, it uses composer as the autoloader.
kind of resolved the issue, the problem was with the <token></token> part.
Since it needs a value of type:guid <Id>guid</Id> that as mentioned in the wsdl and xml request, is a dataset created by a .Net library, and it's used to iterate over the samples, it's quite a complicated request, since it needs previous steps to be fulfilled, so i did the request, by omitting the <token> section

PHP Soap client with ws-security

How should I implement a SOAP client that consumes a service using ws-security.
I have this binding information
<wsHttpBinding>
<binding name="WSHttpBinding_Service" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text"
textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="None" proxyCredentialType="None" realm="" />
<message clientCredentialType="UserName" negotiateServiceCredential="true" algorithmSuite="Default" establishSecurityContext="false" />
</security>
</binding>
</wsHttpBinding>
To my understanding this a .Net configuration and must be mapped to corresponding definitions in PHP.
The wsHttpBinding to my understanding means that SOAP1.1 must be used, and that seems to be working fine.
However, it the security settings are a big problem.
Is there any PHP library that supports WS Security?
I have tried to add a security header to PHP call:
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Username>username</wsse:Username><wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">password</wsse:Password>
</wsse:UsernameToken></wsse:Security>
However, this does not help. Am I missing something?
There are many questions in Stackoverflow on PHP and WS-Security, but I have not found any of them helpful.
EDIT: It turned out that there were problems in the server side.
You can use the vanilla PHP SoapClient (and friends) by using SoapVar to set the SOAP headers. For example;
$objSoapClient = new \SoapClient([...]);
$strXML = <<<XML
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Username>username</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">password</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
XML;
$objAuthVar = new \SoapVar($strXML, XSD_ANYXML);
$objAuthHeader = new \SoapHeader("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", 'Security', $objAuthVar, false);
$objSoapClient->__setSoapHeaders(array($objAuthHeader));

How to retrieve the headers from a SoapFault

I'm using PHP's SoapClient to connect to a webservice (which is out of my control). One of the SoapFaults I'm receiving is as follows (formatted for readability):
<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Infolog xmlns="Infolog" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<InfologMessage xmlns="http://schemas.datacontract.org/2004/07/Microsoft.Dynamics.AX.Framework.Services">
<InfologMessageType>Error</InfologMessageType>
<Message>Customer 6729 not found.</Message>
</InfologMessage>
</Infolog>
</s:Header>
<s:Body>
<s:Fault>
<faultcode>s:Client</faultcode>
<faultstring xml:lang="en-US">Request Failed. See the Exception Log for details.</faultstring>
<detail xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<AifFault xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/Fault" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<CustomDetailXml i:nil="true" />
<FaultMessageListArray i:nil="true" />
<InfologMessageList xmlns:b="http://schemas.datacontract.org/2004/07/Microsoft.Dynamics.AX.Framework.Services" i:nil="true" />
<StackTrace i:nil="true" />
<XppExceptionType>3</XppExceptionType>
</AifFault>
</detail>
</s:Fault>
</s:Body>
</s:Envelope>
When PHP throws the SoapFault exception, the message is the Request Failed. See the Exception Log for details. as would be expected. But what I really need is the Infolog entry from the Header. Is there any way to retrieve the Header params, other than calling __getLastResponse() and parsing the entire XML myself, which seems very clunky?
I am open to switching client libraries if that will do it - I've so far tried with the native PHP SoapClient and Zend\Soap\Client

How to read incoming SOAP request in php

I want to read the SOAP request which is coming from a .NET WinForms application.
The request is:
<?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>
<HotelPricesRQ xmlns="http://zelsoft.ru/">
<HotelPricesRequest>
<Requestor Login="ZLS" Password="DE52F10D5089096E5D83CA559153D64824AAB0B4" />
<Conditions>
<Condition CityID="0" CountryID="0" HotelID="0" AccommodationID="0" RoomCategoryID="0" RoomTypeID="0">
<Created Begin="2013-10-31T00:00:00" End="2013-10-31T23:59:00+04:00" />
</Condition>
</Conditions>
</HotelPricesRequest>
</HotelPricesRQ>
</soap:Body>
</soap:Envelope>
So how to get the Requestor's Login, Password and the Creattion Begin Date?
I have actually build the response, we've test it and when they send me the request they got the response. I'm just sending the responce without reading the request, but I have to read it and log in diffrent users and send different response. So a real example with the request I've posted above will be much much appreciated.
I don't know why I didn't write the solution I got back then for reading the request,
but here it is, in case someone needs it:
$soap_request = file_get_contents("php://input");
Then I just parse it with the PHP XML Parser Functions like so:
$parser=xml_parser_create();
xml_parse_into_struct($parser,$soap_request,$request_array);
xml_parser_free($parser);
Now you have the data in the $request_array as an array.
You can also use The SimpleXMLElement class to get the data from the request.

Barnes and Noble seller SOAP API using PHP

I am new to SOAP and have been trying to connect to Barnes and Noble SOAP API using the php5 built in soap functions.
http://www.php.net/manual/en/class.soapclient.php
My question is, does anyone have any documentation or experience using Barnes and Noble system? I have been going back and forth with the support person and I feel like they assume we should just be able to figure it out.
The faultcode i am getting is "HTTP" and fault string is "Method Not Allowed".
Here is what the support guy says my header should look like.
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>
<SessionInfo xmlns="http://tempuri.org/SessionInfoHeader">
<User xmlns="">your username goes here</User>
<Password xmlns="">your password goes here.</Password>
</SessionInfo>
</soap:Header>
This is as close as i can get it.
<?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:SessionInfo>
<item>
<key>SessionInfo</key>
<value>
<item><key>User</key><value>[username]</value></item>
<item><key>Password</key><value>[password]</value></item>
</value>
</item>
</ns1:SessionInfo>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<searchCriteria/>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I am not even sure this is the problem. Any help would be awesome.
In order to get the child nodes instead of the item key/value pairs you have to pass the parameter as an object.
$client = new SoapClient( 'test.wsdl' );
class SessionInfo {
public $User = 'test#example.com';
public $Password = '12345';
}
$sessionInfo = new SessionInfo();
$soap_headers = new SoapHeader( 'http://tempuri.org/SessionInfoHeader',
'SessionInfo', $sessionInfo );
$client->__setSoapHeaders( $soap_headers );
This will output what the support guy said should work. i think you could also create an array and type cast it to an object but i haven't tried that yet.

Categories