Get SOAP XML before __soapCall? [duplicate] - php

This question already has answers here:
Inspect XML created by PHP SoapClient call before/without sending the request
(3 answers)
Closed 1 year ago.
Is it possible to get the XML generated by SOAP Client before sending it to webservice?
I need this because response from webservice if one of parameters is really wrong I get errors like
Server was unable to read request.
---> There is an error in XML document (2, 408).
---> Input string was not in a correct format.
This typically includes firing up tcpmon or some other tcp watcher utility, capturing the webservice call, copy and paste xml to text editor and go to column 408 to see what's the problem.
I'd really like to simplify this process by getting the XML before sending it.

It is very, very hard (nigh impossible) to do that. What is much easier is using the SoapClient class' built-in debugging functionality to output the request after it has been sent. You can do that like so:
First, when creating your SOAPClient, enable tracing, like so:
$client = new SoapClient($wsdl, array('trace' => true));
Then do whatever processing is necessary to get ready to make the SOAP call and make it. Once it has been made, the following will give you the request you have just sent:
echo("<pre>"); //to format it legibly on your screen
var_dump($client->__getLastRequestHeaders()); //the headers of your last request
var_dump($client->__getLastRequest()); //your last request
And, if you want to see the response as well, the following should work:
var_dump($client->__getLastResponseHeaders()); //response headers
var_dump($client->__getLastResponse()); //the response

Related

What's the right way to handle chunked json response with swagger and autogenerated php client?

I'm a bit lost between definitions, generated code and many things that are a bit of a black box, even after debugging.
But let's start at the beginning. I have an API written with node using NestJS as framework. NestJS automatically creates the swagger/openapi json file. Using swagger-codegen I create a PHP class to access the API from another server. Works like a charm for simple API request.
Now the problem are API request returning a bigger response, i.e. >1000 rows from one or more DBs. Do make the client not wait a long time and create a big JSON response on the server I've switched to NDJSON, which splits the reponse in chunks of smaller JSON parts, each on it's own line. This also works when I create my request by hand with curl or a HTTP wrapper in PHP using fopen and fread. The response type is application/x-ndjson.
But the code generated by swagger-codegen is always waiting until the whole response has been received. It's even worse, because it fails to decode NDJSON with json_decode() and just returns null. Underneath Guzzle is used, which uses PSR7 streams for the response.
Now I could just skip the autogenerated code for the NDJSON endpoints. But I'd prefer not to add special handling and lose all the useful generated checks.
So is it somehow possible to make swagger-codegen give access to the stream of the response? Am I missing a parameter to codegen or something in the swagger JSON? It does have a produce with application/x-ndjson.
Do answer my own question it is possible, but not easily - meaning there is no option or parameter.
First create a class that extends the autogenerated API class. There you have access to all of the protected methods. We assume the endpoint or api method is called testMethodGet. The only thing that can be reused is the request method, i.e. testMethodGetRequest, but it does all of the client side validation and transforming of input data, so that's already a big win. Do also get some of the boilerplate result validation you can copy the "http info method", i.e. testMethodGetWithHttpInfo (use async version if you wish). Remove the if/else block after $responseBody = $response->getBody(); and in the return replace the ObjectSearializer line with just $responseBody.
What you are now getting back is the body as PSR7 stream, but there is one last catch. By default the whole response is drained into a temporary file. To really stream the result body from the server in the created method add an option to $options: $options['stream'] = true; Now the method returns as soon as the body is started. No need to wait for the whole body content.
One more thing. As the result is a PSR7 stream you can use $stream->detach() to get the PHP stream resource if you prefer to use the normal file and stream methods.

How to pass SOAP Request Properties in PHP (as shown in SoapUI)

In SOAP UI I am able to pass a few request properties, specifically Username, Password and WSS-Password Type. They are marked in the screenshot below by a red box:
I've tried passing these values in PHP as the second parameter of the SoapClient function like so:
$soap = new SoapClient('https://rev-int.api.us.fleetmatics.com/Vehicle/SageQuest/VehicleService.svc?wsdl',
array("Username" => "blah#example.com",
"Password" => "notarealpassword",
"WSS-Password Type" => "PasswordText"));
But I get the security error: An error occurred when verifying security for the message.
The WSDL I'm calling itself is here, though my question is about passing the request properties this way in general, really: https://rev-int.api.us.fleetmatics.com/Vehicle/SageQuest/VehicleService.svc?wsdl
It is likely that a security header with username and password is sent along with the message itself, but you can not see this header in the normal window in SoapUI.
Click "http log" in the bottom panel of SoapUI to see the whole communication. Look thoroughly for Header ithems.
If any header ithems, they must be set before your function SOAP call in PHP, but after you've constructed the client.
Use __ setSoapHeaders() to set headers in PHP
I was also getting that security error.
I used the soapui tool as well. But i had better success with the Chrome App called Boomerang.
You need to choose WSS PasswordText under Auth and then everything just seems to work.
Then you can even copy the xml that Boomerang generates into PHP and it works as well.

SoapClient call returns 500 Internal Server Error

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());
}

Invoking WCF service with PHP (with federated security)

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.

slim php (explanation of "request body" documentation)

I am working with Slim PHP for the first time and I am trying to understand one of the concepts. In the slim PHP documentation, it states:
Request Body
Use the request object’s getBody() method to fetch the raw HTTP request body sent by the HTTP client. This is particularly useful for Slim application’s that consume JSON or XML requests.
<?php
$request = $app->request();
$body = $request->getBody();
My question is, what is "the raw HTTP request body"? Is it just a string of all the HTML in the body of the page? What format is it stored as? What would echo $body look like? If I do var_dump($body) I get string(0)"". How do I use it?
I'll just make it an answer rather than comment...
Raw request data is what's submitted from the browser as a body of the POST request.
http://en.wikipedia.org/wiki/POST_%28HTTP%29#Use_for_submitting_web_forms
Technically it can be used to read the data from usual html forms, but this doesn't make much sense as PHP does this good enough and places everything into $_POST.
You may need to read raw data if you have some javascript that sends XML or JSON data, which is not natively accepted by PHP.
The terms you ask for are defined in the RFC2616: Hypertext Transfer Protocol -- HTTP/1.1.
For example, in particular what a Message (Request/Response) Body is: 4.3 Message Body.
If those RFCs are new to you, grab that one an read it from top to bottom and try to understand as much as possible. You'll start to see how those things in the internet work.
Also there is version 2.0 is in the pipe with some changes:
Hypertext Transfer Protocol version 2.0 (Draft 04)
Just in case you're interested.

Categories