So I'm trying to consume a WSDL generated by C# .NET application using PHP. I've ran into multiple issues and have worked my way up to this point where I cannot seem to find the next thing to do.
I'm using SOAP 1.2 to prevent the text/xml error from happening (expected type application/soap+xml), whereas SOAP 1.2 is accepted by the application as text/xml.
The error I receive now is:
[message:protected] => The SOAP action specified on the message, '', does not match the HTTP SOAP Action, 'http://tempuri.org/IMembership/AcquireSecurityToken'.
I'm a little worried why it is not detected any action from my end ('') but I have no clue what I'm doing wrong or what I should be approaching differently.
Underneath you'll find most of the relevant code involved in trying to get this to work for now.
$params = array("soap_version"=> SOAP_1_2,
"trace"=>1,
"exceptions"=>1,
);
$client = new SoapClient("http://removed.for.now/Membership.svc?wsdl", $params);
$actionHeader = new SoapHeader('http://www.w3.org/2005/08/addressing','AcquireSecurityToken', 'http://tempuri.org/IMembership/AcquireSecurityToken',true);
$client->__setSoapHeaders($actionHeader);
$args = array("username"=>"user", "password"=>"goodpw");
try {
$result = $client->AcquireSecurityToken($args);
} catch(Exception $e) {
echo "<h1>Last request</h1>";
// print_r the error report. Omitted for clarity.
}
Any tips or suggestions? Things I'm not doing right or should be trying differently?
I figured it out with the help of some lovely people on IRC.
The ActionHeader that I tried to add using $actionHeader was close, but not correct.
$actionHeader = new SoapHeader('http://www.w3.org/2005/08/addressing', 'Action', 'http://tempuri.org/IMembership/AcquireSecurityToken');
$client->__setSoapHeaders($actionHeader);
Did the trick.
Related
I am trying to connect to an API using PHP and its built-in SoapClient. I have checked against the url I was given through the ill-formatted documents the client gave and $client->__getFunctions() returns a list of three functions. HelloWorld($name), which responds with Hello ~name~, shows me that I am communicating with the server through the SoapClient call and the URL is correct.
However, when I try to access one of the other methods that __getFunctions() gives me, even after copy/pasting the XML from the docs and putting in my own credentials, I am still being given an Internal Server Error faultstring and 500 as faultcode from the SoapFault object.
I am sure that it is my own XML string that is causing the issue but I cannot for the life of me figure out how. Reaching out to the API provider directly hasn't proven helpful. This is my first time dealing with Soap/Web Services so I am unsure of where to go from here.
I did wget http//xxx.xxx.xxx?wsdl and it returned me what looks like a valid XML response, the same one I get when I go directly to the url in the browser. What should I be looking into in order to solve this issue? All of the past API's I've dealt with have been JSON/RESTful so I feel out of my element trying to debug PHP errors.
Edit
I have slowly deleted parts of my method call and parts of my XML string, trying to trigger a different error or something in order to find what I need to fix. What I have found is that by not passing in my XML string, I get a valid response from the $client->FunctionCall(...). It's an "this isn't right" message but it's a message! In fact, passing that function ANYTHING for the xml parameter causes the 500 http faultcode/faultstring. Does this mean that my XMl is poorly formatted or does it mean that there is an issue on their end handling requests?
Second Edit
If I make my $client decleration as follows, I get the faultstring Could not connect to host
$opts = array(
'ssl' => array('ciphers'=>'RC4-SHA')
);
$client = new SoapClient($CREDS['orderingWSDL'], array (
"encoding"=>"ISO-8859-1",
'stream_context' => stream_context_create($opts),
'exceptions'=>true,
));
I am getting more confused the longer I try to fix this.
Sometimes a 500 status coming from a SOAP service could be a SoapFault exception being thrown. To help your troubleshooting, you'll want to be able to inspect both your request XML, and the response XML.
Put your code in try/catch blocks, and use $client->__getLastRequest() and $client->__getLastResponse() to inspect the actual XML.
Example:
$client = new SoapClient('http//xxx.xxx.xxx?wsdl', array('soap_version'=>SOAP_1_1,'trace' => 1,'exceptions' => true));
try {
$response = $client->someFunction();
var_dump($response);
} catch (Exception $e) {
var_dump($e->getMessage());
var_dump($client->__getLastRequest());
var_dump($client->__getLastResponse());
}
I’m trying to invoke a WCF service (.NET) from PHP. It’s a little more complicated than just using a SoapClient since the service uses a WS2007FederationHttpBinding to authenticate.
Here’s the code I’m using at the moment. I haven’t even added credentials as I’m not sure how, but regardless, I’m not even at the point where I’m getting access denied errors.
$wsdl = "https://slc.centershift.com/sandbox40/StoreService.svc?wsdl";
$client = new SoapClient($wsdl,array(
//'soap_version'=>SOAP_1_2 // default 1.1, but this gives 'Uncaught SoapFault exception: [HTTP] Error Fetching http headers'
));
$params = array();
$params['SiteID'] = 123;
$params['GetPromoData'] = false;
$ret = $client->GetSiteUnitData(array('GetSiteUnitData_Request'=>$params));
print_r($ret);
Which WSDL should I be pointing to?
https://slc.centershift.com/Sandbox40/StoreService.svc?wsdl
Seems to be very short, but includes a reference to (note the wsdl0) https://slc.centershift.com/Sandbox40/StoreService.svc?wsdl=wsdl0
https://slc.centershift.com/Sandbox40/StoreService.svc?singleWsdl
Seems to have everything in it.
Do I need to specify SOAP 1.2? When I do, I get a connection timeout ([HTTP] Error Fetching http headers). When I don’t, the default of SOAP 1.1 is used and I get a [HTTP] Cannot process the message because the content type 'text/xml; charset=utf-8' was not the expected type 'application/soap+xml; charset=utf-8'. Is this because I’m not authenticated yet, or because I’m using the wrong SOAP version?
How to authenticate in PHP? Here’s the corresponding .NET/C# code. Do I need to somehow put these as SOAP headers? Or am I thinking about it all wrong, and I need to do some kind of authentication before I even call the method (from what I read, I’m supposed to get a token back and then use it for all future method calls – I think I see an example of this in an answer here on Stack Overflow.
If I call $client->__getFunctions(), using either WSDL and either SOAP version, I’m getting a valid list of all functions, so I assume either of these is fine and my real issue is the authentication.
Other programmers I’ve talked to had spent time trying to get this to work, but gave up and instead implemented a proxy in .NET. They pass their parameters from PHP to their own unsecured .NET service, which in turn calls this secure service. It works, but seems crazily inefficient to me, and counter-productive, as the purpose of WCF is to support all types of clients (even non-HTTP ones!).
I’ve read How to: Create a WSFederationHttpBinding on MSDN, but it didn’t help.
You can use this URL for WSDL https://slc.centershift.com/Sandbox40/StoreService.svc?singleWsdl. This WSDL has all definitions.
You have to use 1.2 because this webservice works with SOAP 1.2 version. I tried it with 1.1 and 1.2 and both of them gived error. 1.1 is version error, 1.2 is timeout error. I think there is an error at this test server. I used it with svcutil to generate code but it gived error too. Normaly it should get information and generate the code example to call service.
Normally you can add authenticate parameters with SoapHeader or directly add to options in SoapClient consruct (if service authentication is basic authentication). I write below code according to your screenshot. But it gives timeout after long wait.
$wsdl = "https://slc.centershift.com/sandbox40/StoreService.svc?wsdl";
$client = new SoapClient($wsdl,array('trace' => 1,'soap_version' => SOAP_1_2));
$security = array(
'UserName' => array(
'UserName'=>'TestUser',
'Password'=>'TestPassword',
'SupportInteractive'=>false
)
);
$header = new SoapHeader('ChannelFactory','Credentials',$security, false);
$client->__setSoapHeaders($header);
$params = array();
$params['SiteID'] = 100000000;
$params['Channel'] = 999;
try {
$ret = $client->GetSiteUnitData($params);
print_r($ret);
}catch(Exception $e){
echo $e->getMessage();
}
__getFunctions works, because it prints functions defined in WSDL. There is no problem with getting WSDL information at first call. But real problem is communication. PHP gets WSDL, generates required SOAP request then sends to server, but server is not responding correctly. SOAP server always gives a response even if parameters or request body are not correct.
You should communicate with service provider, I think they can give clear answer to your questions.
Having worked with consuming .NET WS from PHP before I believe you would need to create objects from classes in PHP that matches the names that .NET is expecting. The WSDL should tell you the types it is expecting. I hope this assist with your path forward!
If the SOAP call works from a C# application, you could use Wireshark (with the filter ip.dst == 204.246.130.80) to view the actual request being made and then construct a similar request from php.
Check this answer to see how you can do a custom SOAP call.
There's also the option of doing raw curl requests, since it might be easier to build your xml body, but then you would have to parse the response yourself with simplexml.
I used Zend Framework 1.11 to make a REST web service in PHP using the Zend_Rest_server class but I wasn't able to intercept and analyze the responses from Zend_Rest_server instances before these are sent to the clients.
To make the REST web service I use this snippet of code:
$server = new Zend_Rest_Server();
$server->setClass('Ws_dummy', 'dummy');
$server->handle();
Is there a method to log responses because I need to analyze them and I wasn't able to find a way to solve this need.
Thank you in advance for any help you can provide.
P.S. For example in SOAP web services I can do this:
$server->setReturnResponse(true);
$response = $server->handle();
or
$server->handle();
$response = $server->getLastResponse();
and analyze the responses
You can log Requests like this:
$writer = new Zend_Log_Writer_Stream('/path/to/logfile');
$logger = new Zend_Log($writer);
$logger->info( Zend_Debug::dump( $_REQUEST, 'Request-Dump', false );
You should do that before the Rest_Server handles the Request.
If you got further Questions - just ask :-)
Edit (added some useful information):
It may be helpful to understand that Zend_Debug::dump() method wraps the PHP function var_dump(). If the output stream is detected as a web presentation, the output of var_dump() is escaped using htmlspecialchars() and wrapped with (X)HTML pre-tags.
Edit #2:
You can return the Response of Zend_Rest_Server with:
$server->returnResponse(true);
before $server->handle().
Edit #3:
Be aware:
If I've read everything right you need to send the Headers by urself if you are returning the Response.
You can get/+set the Headers with:
$headers = $server->getHeaders();
foreach( $headers as $header ) header( $header );
I am trying to communicate with TargetProcess via their Soap services. When I do a simple request such as a "read" it works.
For example
// Some code...
$client = soapclientnusoap(path to my service...);
$client->call("RetrieveAll");
// Get nice results...
Next.. when I try to do a bit more complex operation, it works too...
// Some code...
$client = soapclientnusoap(path to my service...);
$params = array("Name"=>"Sample project");
$client->call("CreateProject", $params);
// Get nice results...
It works too..
HOWEVER, when I do nested arrays, it fails
// Some code...
$client = soapclientnusoap(path to my service...);
$params = array('entity'=>
array("Name" => "Bug name")
);
$client->call("CreateBug", $params);
// Error..
The only difference is that the basic commands don't have params or have a one level of params, and the last one has a nested params.
Any clues ?!!!
Thank you.
This is my error
[faultstring] => Server was unable to read request. ---> There is an error in the XML document. ---> Input string was not in a correct format.
And this is the request (headers removed)
<SOAP-ENV:Body><Create xmlns="http://targetprocess.com"><entity><ID/><BugID/><Name>TEST Bug</Name><Description>Some words about the bug</Description><StartDate/><EndDate/><CreateDate>2011-02-11T17:57:21</CreateDate><ModifyDate/><LastCommentDate/><NumericPriority/><Effort/><EffortCompleted/><EffortToDo/><TimeSpent/><TimeRemain/><LastCommentUserID/><OwnerID/><LastEditorID/><EntityStateID/><PriorityID/><ProjectID>131</ProjectID><IterationID/><ParentID/><ReleaseID/><SeverityID/><BuildID/><UserStoryID/></entity></Create></SOAP-ENV:Body></SOAP-ENV:Envelope>
Notes:
I am using the latest Nusoap version on php5.2 that means I had to rename soapclient from nusoap, tu soapclientnusoap.
I can't use native php5 SoapClient because the service I am accessing is requesting some headers and to be honest, I already spent 5 hours and was not able to generate the headers as expected.. maybe I should focus on this but I can't think on what else to do, so I am stuck with Nusoap.
Hello I'm having problems sending arrays, structs and arrays of structs from PHP to an ASP.NET SOAP server...
Anyone have a sollution for this? I've googled for days and any sollution worked for me. Perphaps I'm forgetting something...
There are examples of my code:
$client = new SoapClient($options);
$pCriteria = new stdClass();
$pCriteria->type=1;
$pCriteria->capacity=4;
//Test 1 (fail):
$resp = $client->GetRooms(array("pCriteria"=>$pCriteria));
//Test 2 (fail):
$resp = $client->GetRooms(array("pCriteria"=>new SoapVar($pCriteria, SOAP_ENC_OBJECT, "TCriteria", "http://www.w3.org/2001/XMLSchema")));
print_r($resp);
I don't know how to code functions that require an array of TCriteria (TCriteria[], TCriteria_Array type) either... i've tried sending the raw array, a SoapVar with SOAP_ENC_ARRAY encoding and TCriteria_Array type, ... but it does not work (the SOAP server becomes unavaiable and needs to be restarted).
I've tried creating classes for the complex types too, instead of stdClass, but not working.
I don't know where's the problem. The server admins cannot help me and I haven't found any sollution over internet. I'm a bit desperate hehe.
Can you help me please? Can you provide samples of code with the three cases (array of simple data, array of struct and struct) ? Thanks!
I had a similar situation with a PHP Soap Client communicating with a .NET Soap Server using WSDL 2.0. Here's one thing I discovered: When passing the information to the server, you must explicitly define the variable as a SoapVar object. So in your example above, change it to:
$pCriteria->type = new SoapVar(1, XSD_INT, 'xsd:int');
Passing an array is similar, essentialy you pass an array of SoapVars:
$pCriteria->type = array(new SoapVar(1, XSD_INT, 'xsd:int'), new SoapVar(2, XSD_INT, 'xsd:int', new SoapVar(3, XSD_INT, 'xsd:int'));`enter code here`
Also, you can use several built-in functions of the SoapClient to get some additional feedback on possible errors.
$client->__getLastRequest() //To display the XML that you sent to the server
$client->__getLastResponse() //to display the XML that is sent in response to your request
If you can get a copy of the expected WSDL format you can use the response from the above commands to determine what is going wrong. Usually you can access this from the URL that you pass to the SoapClient. So, for example, if the WSDL services URL is http://example.com/webservices/wvrgroupservice.asmx?WSDL, enter http://example.com/webservices/wvrgroupservice.asmx to view the functions and expected XML from that server.