WCF - "Incomplete" WSDL with reference to another WSDL - php

I am using SoapClient to send some data from a PHP site to a .Net WCF service.
This is my (shortened for clarity) code:
$wsdl = '/var/www/libraries/MyWsdl.xml';
$myClient = new SoapClient($wsdl);
and later, the actual call:
try {
$res = $myClient->Foo($someParameter);
}
catch(SoapFault $e){
//...
}
catch(Exception $e){
//...
}
This works great when everything is online, and the error handling works if the destination server is down on the time Foo is called.
Problem is that the SoapClient constructor fails, if the destination server is down, even though i've provided it with a static XML file with the WSDL (in oppose to a URL like "http://www.destination.com/MyService?wsdl").
I believe this is happening because the WSDL contains a reference to another WSDL:
<wsdl:import namespace="http://MyCompany.Services" location="http://www.destination.com/MyService?wsdl=wsdl0"/>
This other WSDL contains the definitions of the call parameters.
So, how can I "Inline" the second "sub-WSDL" inside the original one?
Will this allow me to create a SoapClient without initiating a connection to the destination server?
This is my service definition:
[ServiceContract(Namespace = "http://MyCompany.Services")]
public interface IMyService
{
[OperationContract]
[XmlSerializerFormat(Style = OperationFormatStyle.Rpc, Use = OperationFormatUse.Literal)]
string Foo(string myParameter);
}

You can use the single WSDL-file extension from here: http://wcfextras.codeplex.com/
Note that when using .net4.5 there should be no need for it, as the default metadata endpoint now also generates single WSDL-files in addition to the linked versions.
To do that simply add "?singleWSDL” to the URI (source: http://msdn.microsoft.com/en-us/library/dd456789(v=vs.110).aspx)

Related

How to globally override the SoapClient class?

I use external libraries to communicate with external systems. Communication is via Soap.
I provide the library with a set of data in a simple way. She checks the data, sends the request, and receives the reply. Converts the response to its object and returns.
How can I access the SoapClient object without making changes to the external library?
For this object, I need the original data. Request, response, headers.
Is it possible to do?
EDIT:
A simple example of using one of the many external libraries:
class fedex {
public function trackShipment($number)
{
$trackRequest = new TrackServiceTrackRequest();
$trackRequest->WebAuthenticationDetail->UserCredential->Key = $this->getAccessNumber();
$trackRequest->WebAuthenticationDetail->UserCredential->Password = $this->getAccountPassword();
$trackRequest->ClientDetail->AccountNumber = $this->getAccountNumber();
$trackRequest->ClientDetail->MeterNumber = $this->getMeterNumber();
$trackRequest->SelectionDetails[0]->PackageIdentifier->Value = $number;
$request = new TrackServiceRequest();
return $request->getTrackReply($trackRequest);
}
}
$fedex = new fedex();
$result = $fedex->trackShipment('123456789');
How to get original xml with request and header that was sent by library without modifying it?
The TrackServiceRequest() object does not access the SoapClient() object.

using functions from internal SOAP interface via php

I received a manual to internal SOAP interface of my partner. It says:
MyPARTNER web services are provided in the form of a SOAP interface. The service is available in this URL: https://justsomeurl.com:435/soap
then some bla bla about authorization etc. and then a part about Accessible Methods:
pull()
The PULL method is used for pulling data from the database. The method
receives a unique data based parameter under an internal name
requestXML. This parameter contains data in a structured XML format.
String pull(String requestXML)
The XML contains data required to make the request, and the response
data is sent back.
then some other methods, error codes, it's not important here...
The problem is that I'm totally unexperienced in SOAP so I don't know how to use this interface via PHP. I've tried to find some examples, tutorials and I am now little bit more informed about SOAP and its functionality but still haven't found any advice about how to use interface like this...
thanx for any help
Php comes with PHP SOAP libraries, that usually are included and enabled after a common php installation.
Yuo are asked to biuld the client part of the webservice pattern. Your partner should provide you the .wsdl of the web service. The wsdl describes the avialble method, the parameters they need and what they return.
Tipically parameters and return values are array structures
This could be a skeleton for your code:
//build a client for the service
$client = new SoapClient("partner.wsdl");
//$client is now a sort of object where you can call functions
//prepare the xml parameter
$requestXML = array("parameter" => "<xml>Hello</xml>");
//call the pull function this is like
$result = $client->__soapCall("pull", $requestXML );
//print the value returned by the web service
print_r($result);
Here follows a non-wsdl example
First the location paramater is the address the SOAP request will be sent to.
The uri parameter is the target namespace of the SOAP service. This is related to xml namespaces.
A sample code for you could be:
//for URI specification you should watch your partners documentation. maybe also a fake uri (like mine) could work
//build a client for the service
$client = new SoapClient(null, array(
'location' =>
"https://justsomeurl.com:435/soap",
'uri' => "urn:WebServices",
'trace' => 1 ));
// Once built a non-wsdl web service works as a wsdl one
//$client is now a sort of object where you can call functions
//prepare the xml parameter
$requestXML = array("parameter" => "<xml>Hello</xml>");
//call the pull function this is like
$result = $client->__soapCall("pull", $requestXML );
//print the value returned by the web service
print_r($result);
Here a useful link: http://www.herongyang.com/PHP/SOAP-Use-SOAP-Extension-in-non-WSDL-Mode.html

SOAP Client Error: "Error Fetching Http Headers"

I am trying to use a SOAP Client-Server in my computer and it doesn't look like it is going to work, I am getting this error Error Fetching Http Headers when I try to run my SOAP Client.
I have been looking and the solution that I have encountred is to increase the default_socket_timeout from 60 to 120 seconds and it doesn't work for me, also I have seen another solution that is putting the vhost in my apache KeepAlive Off and that didn't work.
The WSDL is working fine because I try to use it in another computer and it work.
I am running PHP Version 5.3.5-1ubuntu7.4 in Linux Mint using Zend Framework, I hope some of you can help me fix this thank you.
I'm sorry but I don't know what you are using to set up your SOAP service.....
If you can give more information about your SOAP service (poss Zend_Soap given the Zend Framework tag) etc that would be great.
Also, as a quick alternative, you say you've looked at the WSDL on another computer, perhaps try the application in an alternative environment to ensure it's not an environment issue.
May be a simple issue with your client-server code.
UPDATE: Ok so I realised the example I mentioned yesterday wasn't fully implemented so I've hacked something together quickly that you can try to see if it works in your environment.
The code is a mix of something I found here (an example of Zend_Soap_Server) and something from another SO question here (an example of a basic SOAP service test).
I've tested it at my end using ZF 1.11 and the example I'm outlining uses the default Application path you get with a new ZF project (e.g models are in directory application/models so the model shown is headed up Application_Model_Classname).
If it works, you can tweak accordingly....if it doesn't work we can try something else.
Start by creating a new SOAP controller and set the class up like this:
<?php
class SoapController extends Zend_Controller_Action
{
public function init()
{
ini_set("soap.wsdl_cache_enabled", "0"); //disable WSDL caching
$this->_helper->layout()->disableLayout(); //disable the layout
$this->_helper->viewRenderer->setNoRender(); //disable the view
}
public function indexAction ()
{
if (isset($_GET['wsdl'])) {
//return the WSDL
$this->handleWSDL();
} else {
//handle SOAP request
$this->handleSOAP();
}
}
private function handleWSDL ()
{
$strategy = new Zend_Soap_Wsdl_Strategy_AnyType();
$autodiscover = new Zend_Soap_AutoDiscover();
$autodiscover->setComplexTypeStrategy($strategy);
$autodiscover->setClass('Application_Model_SoapService');
$autodiscover->handle();
}
private function handleSOAP ()
{
$server = new Zend_Soap_Server(null,
array('uri' => "http://YOURDOMAIN/soap?wsdl"));
$server->setClass("Application_Model_SoapService");
$server->handle();
}
public function testAction()
{
$client = new Zend_Soap_Client("http://YOURDOMAIN/soap?wsdl");
try {
echo $client->testMethod('test');
} catch (Exception $e) {
echo $e;
}
}
}
In the class above, the WSDL is automatically generated using Zend_Soap_Autodiscover with a SoapService.php file at application/models/SoapService.php used as the template. Note the DocBock comments above each method in your target class are integral to this process.
Next create the SoapService.php file in the default models folder:
<?php
class Application_Model_SoapService
{
/**
* testMethod
*
* #param string $string
* #return string $testSuccess
*/
public function testMethod(string $string)
{
$testSuccess = 'Test successful, the message was: ' . $string;
return $testSuccess;
}
}
If all is working as it should be you can visit:
http://YOURDOMAIN/soap?wsdl
to see the WSDL and visit:
http://YOURDOMAIN/soap/test
to get a success message with the string you specified in the client request within the testAction() code in the SoapController class as part of the message.
Let me know if it's working or not and we can go from there.
I'll be able to have another look on Monday.

Specifying SOAP Headers for a Zend_Soap Service

I have a generally straight forward web service that I've written (converting code to ZF from a Java implementation of the same service and trying to maintain the same wsdl structure as much as possible). The service loads a PHP class, rather than individual functions. The PHP class contains three different functions within it.
Everything seems to be working just fine, except that I can't seem to figure out how to specify that a given function parameter should be passed as a SOAP header. I've not seen any mention of SOAP headers in the Server context, only how to pass header parameters with a client to a server.
In addition to the standard parameters for the function that would be sent in the SOAP body and detailed in the docblock, I would like to specify two parameters (a username and password) that would be sent in a SOAP header.
I have to assume this is possible, but haven't been able to find anything online, nor have I had any responses to a similar post on Zend's forum. Is there something that can be added in the docblock area to specify a parameter as a header (maybe in a similar fashion to using WebParam?)? Any suggestions/examples on how to get this accomplished would be greatly appreciated!
I just ran into this problem myself. My SOAP request is structured like so:
<SOAP-ENV:Envelope>
<SOAP-ENV:Header>
<Header>
<APIKey>$key</APIKey>
<SiteID>$id</SiteID>
</Header>
</SOAP-ENV:HEADER>
(body)
</SOAP-ENV:Envelope>
Because the contents of my <SOAP-ENV:Header> tag are in the <Header> enclosure, I created a public method in the class my SoapServer instance loads called Header that then sets a private class variable to true if the API key and Site ID are valid. The other methods in my class that process the body of the request then check to see if that variable is true before proceeding. Ugly, I know, but as you mention, there's no documentation, and this seems to be the easiest way. It looks like this:
class MySoapRequestHandler
{
private $authenticated;
public function Header($data)
{
//your logic here
if($request_is_valid)
{
$this->authenticated = true;
}
else
{
$this->authenticated = false;
}
}
public function ProcessBody($data) //of course named whatever your body element is named
{
if($this->authenticated === true)
{
//process the request
}
else
{
//throw a soap fault?
}
}
}
Let me know if you have more questions; happy to help as much as I can.

Inspect XML created by PHP SoapClient call before/without sending the request

The question:
Is there a way to view the XML that would be created with a PHP SoapClient function call BEFORE you actually send the request?
background:
I am new to WSDL communication, and I have a client who wants me to develop in PHP, a way to communicate with a WSDL service written in ASP.NET. I have gotten pretty far, but am running into an issue when it comes to passing a complex type. I have tried a couple of different things so far.
1) Setting up a single array such as $params->Person->name $params->Person->address
2) Setting up a single array $Person = array('name'=>"joe",'address' = "123");
then passing into the call as a param "Person" => $Person;
and a few others. But every time I get the error
SoapException: Server was unable to
process request ---> System.Exception:
Person is Required. at service name.
In order to further the troubleshooting, I would like to see the XML document that is being sent to see if it is creating a complex type in the way I am expecting it to.
I am creating the service using $client = new SoapClient('wsdldoc.asmx?WSDL'); calling it with $client->CreateUser($params); and then trying to see it using the function $client->__getLastRequest(); but it never makes it to the __getLastRequest because it hits a fatal error when calling CreateUser($params).
The question again:
Is there any way to view the XML created by the CreateUser($params) call WITHOUT actually sending it and causing a fatal error
Upfront remark: In order to use the __getLastRequest() method successfully, you have to set the 'trace' option to true on client construction:
$client = new SoapClient('wsdldoc.asmx?WSDL', array('trace' => TRUE));
This way, your request will still be sent (and therefore still fail), but you can inspect the sent xml afterwards by calling $client->__getLastRequest().
Main answer:
To get access to the generated XML before/without sending the request, you'd need to subclass the SoapClient in order to override the __doRequest() method:
class SoapClientDebug extends SoapClient
{
public function __doRequest($request, $location, $action, $version, $one_way = 0) {
// Add code to inspect/dissect/debug/adjust the XML given in $request here
// Uncomment the following line, if you actually want to do the request
// return parent::__doRequest($request, $location, $action, $version, $one_way);
}
}
You'd then use this extended class instead of the original SoapClient while debugging your problem.
I found this thread while working on the same problem, and was bummed because I was using classes that already extended the SoapClient() class and didn't want to screw around with it too much.
However if you add the "exceptions"=>0 tag when you initiate the class, it won't throw a Fatal Error (though it will print an exception):
SoapClient($soapURL, array("trace" => 1, "exceptions" => 0));
Doing that allowed me to run __getLastRequest() and analyze the XML I was sending.
I don't believe there is a way that you'll be able to see any XML that's being created... mainly because the function is failing on it's attempt to create/pass it.
Not sure if you tried already, but if you're having trouble trying to decide what exactly you need to pass into the function you could use:
$client->__getTypes();
http://us3.php.net/manual/en/soapclient.gettypes.php
Hope this helps!

Categories