I want to run a report from JasperServer using a PHP SOAP client. I found this example online, but I want to attach an XML data source, used for the report data, and I am unsure how it should be correctly attached.
How can I attach my XML data source to the SOAP request, that is acceptable to Jasper Server?
public function requestReport($report, $format, $params) {
$params_xml = "";
foreach ($params as $name => $value) {
$params_xml .= "<parameter name=\"$name\"><![CDATA[$value]]></parameter>\n";
}
$request = "
<request operationName=\"runReport\" locale=\"en\">
<argument name=\"RUN_OUTPUT_FORMAT\">$format</argument>
<resourceDescriptor name=\"\" wsType=\"\"
uriString=\"$report\"
isNew=\"false\">
<label>null</label>
$params_xml
</resourceDescriptor>
</request>
";
$client = new SoapClient(null, array(
'location' => $this->url,
'uri' => 'urn:',
'login' => $this->username,
'password' => $this->password,
'trace' => 1,
'exception'=> 1,
'soap_version' => SOAP_1_1,
'style' => SOAP_RPC,
'use' => SOAP_LITERAL
));
$pdf = null;
try {
$result = $client->__soapCall('runReport', array(
new SoapParam($request,"requestXmlString")
));
$pdf = $this->parseReponseWithReportData(
$client->__getLastResponseHeaders(),
$client->__getLastResponse());
} catch(SoapFault $exception) {
$responseHeaders = $client->__getLastResponseHeaders();
if ($exception->faultstring == "looks like we got no XML document" &&
strpos($responseHeaders, "Content-Type: multipart/related;") !== false) {
$pdf = $this->parseReponseWithReportData($responseHeaders, $client->__getLastResponse());
} else {
throw $exception;
}
}
if ($pdf)
return $pdf;
else
throw new Exception("Jasper did not return PDF data. Instead got: \n$pdf");
}
The full example I found here https://gist.github.com/26205
The goal it to create something like this:
This is more a comment than an answer, but probably helpful. There is a library called WSO2 WSF/PHP:
WSO2 WSF/PHP is intended to fill some of the gaps in the PHP extension. WSO2 WSF/PHP is an open source implementation like the SOAP extension and supports MTOM, WS-Addressing, WS-Security, and WS-RelaiableMessaging. WSO2 WSF/PHP supports a similar API to that of the SOAP extension. There are plans to wrap the API to provide the same API of the SOAP extension; it will be written in C.
I think you're looking for Binary attachment (MTOM).
The following links might be useful as well:
PHP SOAP Messages with AttachmentsPEAR::SOAP related
MIME Attachments using SoapClient ClassMailing List
SOAP Messages with AttachmentsW3C
PHP SOAP ExtensionIntroduction
On their own site they have also some examples about integration to Web Services via php.
Is there any help for this?
SOAP request has no attachment support.
The idea is how you process your request.
The only way I use SOAP requests with attachments is to Base64 Encode the data to be attached, and add it to a Text node.
Add the tag with attribute encoded="true/false". If its a file content, supply the name of the file in the request.
In the server side, if you find the node with an attribute encoded="true", You can take the data from the node, Base64Decode it and do what ever you need.
The idea of Base64 is to avoid many special characters that a SOAP request doesn't support in Request. Some SOAP processors have the option with "encoded" attribute.
You want to include a xml file in your SOAP query or response?
You could encode it base64 like in emails and then require the user on the other end to decode it.
$data = chunk_split(base64_encode($xml_data));
Then just add it in a seperate xml tag in your SOAP query/response.
Related
I know very few things about XML and SOAP requests, but I am in a situation I have to consume a web service using PHP to send and receive data for my app, so any help would be appreciated.
The web service is a WSDL service, it exposes a function called processIncomingMessage() and expects the SOAP envelope body to look like this:
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<processIncomingMessageRequest xmlns="http://icis.externaldomain.services.ws">
<!-- Optional -->
<messageRequest xmlns="">
<DigitallySignedMessage>
<xmlMessage>[anyType]</xmlMessage>
</DigitallySignedMessage>
</messageRequest>
</processIncomingMessageRequest>
</Body>
</Envelope>
So when I get the form values in JSON, I proceed like this:
//convert JSON to array
$data = json_decode($json, true);
//convert array to XML
$xmlMessage = ArrayToXml::convert($data, $root);
//set the function arguments
$messageRequest = [
"messageRequest" => [
"DigitallySignedMessage" => [
"xmlMessage" => $xmlMessage,
"isXmlString" => $isXmlString,
"messageType" => $messageType
],
"traderID" => $traderID,
"wsPass" => $wsPass,
"wsUserID" => $wsUserID
]
];
//create soap client instance
$client = new SoapClient($wsdl, $soapOptions);
//call the SOAP function and save the response in a var
$response = $client->processIncomingMessage($messageRequest);
The ArrayToXml makes use of the DOMDocument class to create the xml and
I fount it here: https://github.com/spatie/array-to-xml/blob/master/src/ArrayToXml.php
Now, even though I validated the XML created above against the provided xsd file and on the web service's website with their own tool, the SOAP response looks like this:
status: Invalid Argument
explanation: XML Message String but class is class com.sun.org.apache.xerces.internal.dom.ElementNSImpl
I have no idea what I am doing wrong here...
Could anybody shed some light?
I am newbie to services, i am currently calling .net WCF service in PHP using SoapClient object. Code is as follows:
$arr2=array('paymentVendorCode'=>"test1",'transactionNumber'=>123456789,'dataSource'=>'Ghana_QA','customerNumber'=>45678912,'amount'=>10,'invoicePeriod'=>1,'currency'=>'GHC','paymentDescription'=>'CashPayment','methodofPayment'=>'CASH','productCollection'=>'PRCC4');
$certFile = "certs/Entrust.pem";
$options = array('soap_version' => SOAP_1_1 , 'local_cert' => $certFile , 'exceptions' => true ,'trace' => true ,'wdsl_local_copy' => true ,'ssl' => array('verify_peer' => false) ,'https' => array('curl_verify_ssl_peer' => false, 'curl_verify_ssl_host' => false)
);
try
{
echo "SOAP Client Object Made <br />";
//To Make soap client using WSDL files offline
$client = new SoapClient("RTPP_Web_Service_WSDL_20130306/multichoice.paymentservice.wsdl",$options);
}
catch(SoapFault $E)
{
echo "Error:--> ".$E->faultstring;
}
print_r($client->__getFunctions());
try
{
echo $pmt_customer=$client->__call("SubmitPayment", $arr2)."<br /><br />";
}
catch(SoapFault $fault)
{
echo "came here in catch";
trigger_error("SOAP Fault:(faultcode: {$fault->faultcode}\n"."faultstring: {$fault->faultstring})", E_USER_ERROR);
}
I have go through all WSDL files i am using and got all elements, messages, operations, parameters etc. In one of the WSDL file soap address is as follows:
<soap:address location="https://wispqa.multichoice.co.za/PaymentServicePerimeter/Intermediate.svc" />
which is actual address of service being called, normally services are called with ?WSDL at the end of url but even after adding this at the end of above url the service page appearance remains same.
One thing in service documentation is written as "The service currently does not make use of message contracts."
I am sending request to service calling one of its methods from the list that i got by calling "$client->__getFunction()". But instead of response it is giving fatal error that can be seen from the screenshot at http://i.stack.imgur.com/GvS3d.png.
I have been working on it for a almost a week but got stuck here. Please if anybody can get me out of here. Thanks in advance.
I got my answer, What i was missing is soap action for the method to be called. I have given soap action by visiting WSDL files thoroughly and found soap action for method i was calling as:
$submitpayment_action = "http://MultiChoice.PaymentService/ServiceContracts/PaymentServiceContract/SubmitPayment";
Also i have changed format of my request by using xml format whose excerpt is as follows(i have got this xml format by using SOAPUI tool):
$submitpay = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"
xmlns:mes=\"http://MultiChoice.PaymentService/MessageContracts\"
xmlns:ser=\"http://MultiChoice.PaymentService/ServiceContracts\"
xmlns:dat=\"http://MultiChoice.PaymentService/DataContracts\"
xmlns:mcom=\"http://mcomp.scontracts\" xmlns:mcom1=\"http://mcomp.dcontracts\">
<soapenv:Header/>
<soapenv:Body>
<mes:SubmitPaymentRequest>
<ser:PaymentRequest>
<dat:paymentVendorCode>".$vendorcode."</dat:paymentVendorCode> .......
and finally using this "__doRequest" SOAP method to call service method as:
$response = $client->__doRequest($submitpay,$location,$submitpayment_action,SOAP_1_1,$one_way = 0);
print_r($response);
This show me the desired response. Now one can extract required information from the response using "simplexml_load_string($response);" , "registerXPathNamespace()" and "xpath('//string');" methods.
I have taken over an application from a previous developer, and there was some code which was half finished using Nusoap, I am getting the error:
Call to register_donation() web service failed due to an exception:
Function ("register_donation") is not a valid method for this service
The application is built on: CakePHP 1.2.10 & using nuSoap 0.9.5.
I have already ini_set('soap.wsdl_cache_enabled', 0); (This doesnt help.)
My code is below (I shortened it for readability), Am I processing this response correctly?
(The code was taken over by myself from a previous developer).
The Pastebin (of the full code) is here: PasteBin Link
A shortened version is below for a glance over, the full version is in the pastebin link above.
<?php
ini_set('soap.wsdl_cache_enabled', 0);
class ServicesController extends AppController
{
var $uses = array("Donation");
var $components = array( "Email", "MatchingEvents" );
function registerDonation($donation = null){
$this->log('hit registerDonation', 'donation');
$this->autoRender = false;
App::import('Vendor','nusoap');
Configure::write('debug',0);
Configure::write('Session.start', false);
//init soap server
$server = new soap_server();
$endpoint = 'http://new.mysite.com/services/registerDonation';
//initialize WSDL support
$server->configureWSDL('donations', 'urn:donations', $endpoint);
//setup service type
$server->wsdl->addComplexType(
'DonationResult',
'complexType',
'struct',
'all',
'',
array(
'success' => array('name' => 'success', 'type' => 'xsd:boolean'),
'msg' => array('name' => 'msg', 'type' => 'xsd:string'),
'error_number' => array('name' => 'error_number', 'type' => 'xsd:string')
)
);
//register the method to expose
$server->register('register_donation',
array('ct_number' => 'xsd:string', 'project_id' => 'xsd:int', 'donation_amount' => 'xsd:decimal',
// Stripped all other params
),
array(
'result' => 'tns:DonationResult'
),
'urn:donations',
'urn:donations#register_donation',
'rpc',
'encoded',
'Accepts the results of a donation to a charity or project on the site'
);
//This inner function is registered and then called (keep within outer function!)
function register_donation(
// Pass in all the params (Stripped for readability)
$ct_number = null, $project_id = null, $donation_amount = null
){
// This function is never hit!.. its not registered, why?
$this->log('hit #3-register_donation called!', 'donation');
$return = $this->Donation->add(array(
'unique_id' => $unique_id,
'ct_number' => $ct_number,
'project_id' => $project_id,
'donation_amount' => $donation_amount,
// Pass in all other params, (Stripped for readability)
));
// Process that request
$log = $this->Donation->log . 'Result: ' . $return['msg'] . "\n";
$this->log( $log, 'donation' );
if ( isset($this->Donation->matching['events']) )
{
//Reserved donations should already have had their events handled
$this->MatchingEvents->handle($this->Donation->matching, !$this->Donation->matching['reserved']);
}
if ( !empty($this->Donation->cacheSaved) )
$this->_sendDonationEmails($this->Donation->cacheSaved);
return $return;
}
$HTTP_RAW_POST_DATA = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : '';
$server->service($HTTP_RAW_POST_DATA);
}
function _sendDonationEmails($donation)
{
// Send the emails
}
}
?>
If there is any more information I can provide, please let me know.
To Summarise: How do I process a nusoap response coming from a external source.
Any debugging ideas, hints & tips or solution will be rewarded (+1!).
In the code posted there's a function named registerDonation inside a controller named ServicesController. Inside that function there is another function called register_donation (This obviously doesn't make sense [Old Code]).
Remove the function named register_donation out of the registerDonation method and place it as a method inside the ServicesContoller.
Then change the following line (where you register the function to expose).
From: $server->register('register_donation',
To: $server->register('ServicesController.register_donation',
So you're calling class.method as opposed to method only (This would work in PHP4 procedural programming, but in OOP, you need to specify the controller.method when exposing the function).
Having other problems?
Q: CakePHP & nuSOAP is not hitting my controller/method, but redirects to app/webroot
A: Using CakePHP 1.2, I found when it did not have a services model (because it wasn't
technically required), The request does not hit the controller. So if you're having this issue, create a model for your controller, even if you're not using it. For your reference, here a modal example:
<?php // You may not need this modal, but cakephp requires it to be there.
class Services extends AppModel {
public $name = 'Services';
public $useTable = false;
}
?>
Q:What URL should I be receiving responses to?
A: The URL you set your response to hit must include ?wsdl.
So.. for eg: you have a controller named SOAPController and method named process.
In CakePHP, your URL would look like this: mydomain.com/SOAP/process.
So your WSDL is located at mydomain.com/SOAP/process?wsdl. Make sure your callback URL is set to this (including the wsdl).
Q: How do I debug SOAP requests/responses using CakePHP?
A: CakePHP has a logging feature which proved invaluable in debugging SOAP. In your controller (or modal) you can use:
$this->log('your message or variable', 'name_of_file_to_save_to);
You can use this through your SOAP request or response to see what parts are being hit/called and debugging variables (eg: $HTTP_RAW_POST_DATA).
Q: My WSDL location is shown as `domain.com/app/webroot` when I visit the SOAP page.
A: I thought this was the issue causing all the problems, Looking at the source code Nusoap uses PHP_SELF to get the current script (which in cakePHP is app/webroot/index.php), Don't worry about this, you can access the wsdl by appending the URL with ?wsdl, This will show you your generated WSDL file, You don't need to worry about fixing this. Its not interfering with your SOAP request whatsoever, It's merely there for your convenience.
Q: My SOAP address location is showing 'domain.com/app/webroot/index.php`
A: This was a issue I fixed by including the $endpoint in the configureWSDL().
//Set our endpoint, Replace with your URL
$endpoint = 'http://yourdomain.com/controller/method';
//initialize WSDL support - Include the $endpoint.
$server->configureWSDL('donations', 'urn:donations', $endpoint);
// Now your Soap Address Location should be what you set as the $endpoint
I can not seem to find out how to set an attribute to a SOAP request without using the XSD_ANYXML encoding.
The request parameter should look as follows
<request
xmlns:ns="/some/ns">
...
<ns:parameter attr="some attribute">
value
</ns:parameter>
...
</request>
Of course the following code works, but it's rather ugly (ugly, because it uses string concatenation where it should use the SOAP_Client API and because it does not use the general namespace)
$param = new SoapVar(
'<ns_xxx:parameter xmlns:ns_xxx="/some/ns" attr="some attribute">
value
</ns_xxx:parameter>',
XSD_ANYXML
);
Is there a better way to create a SOAP request parameter with a namespace and an attribute?
I am looking for s.th. like the following (this is just some pseudo code using the SoapVar API):
$param = new SoapVar(
array(
'_' => 'value',
'attr' => 'some attribute'
),
SOME_ENCODING,
null,
null,
null,
'/some/ns'
);
For this, you need to derived the class from SoapClient and Override the method __doRequest():
class ABRSoapClient extends SoapClient {
// return xml request
function __doRequest($request, $location, $action, $version) {
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->preserveWhiteSpace = false;
$xml= $dom->loadXML($request);
// Goto request Node and Set the attribute
$attr_ns = $dom->createAttributeNS('xmlns:ns', '' ); // instead of xmlns:ns use Namespace URL
$attr_ns->value = '/some/ns';
// add atribute in businessReport node
$dom->getElementsByTagName($report_type)->item(0)->appendChild( $attr_ns );
$request = $dom->saveXML();
return parent::__doRequest($request, $location, $action, $version);
}
}
$client = new ABRSoapClient(.....);
$save_result = $client->request($param);
// You can check the form request using function
$client->__getLastRequest();
I hope this will resolve your problem.
SOAP does not support attributes, may be you should use REST instead!
EDIT:
Please check the body style w3c:"4.3 SOAP Body" and remember that
you need to encode your message with "soap-envelope" namespace and describe
your XML types thats why, you can't use attributes to describe your message data.
But if you ask me, it can be made possible! You can use a custom SoapClient parser or something like that and convert your message as you like it.
A example of that may be RSS over SOAP http://www.ibm.com/developerworks/webservices/library/ws-soaprdf.
But, the problem would be that you would miss the descriptive information about your message data/types and other clients could not easy understand your messages!
My best practice for you would be to use elements instead of attributes,
i know you need to fix your XML schema but thats the way it goes or switch to a other technology.
SOAP 1 does support attributes. Here is an example of Perl code using both attributes and values (from a client):
$som = $client->call(
'tran:getContent',
SOAP::Header->name('cred:credentials')->attr({
'username' => $username,
'password' => 'xxx',
'customerID' => 'xxx'}
),
SOAP::Data->name('contentID')->value('9999')
)
When consuming a .NET WCF webservice I get the following response (error):
Unsupported HTTP response status 415
Cannot process the message because the content type 'text/xml; charset=UTF-8'
was not the expected type 'application/soap+xml; charset=utf-8'.
How do I change the content type? I can't find it in the NuSOAP forums/docs, or I might be overlooking something....
i know this is an old post, but i ran in to this page looking for an answer.
application/soap+xml is the content-type passed when using SOAP 1.2,
text/xml is used with SOAP 1.1,
something like this should do the trick,
$client = new SoapClient("some.wsdl", array('soap_version' => SOAP_1_1));
You can specify the encoding of NuSOAP streams with the webservices like that :
$client = new nusoap_client($params);
$client->soap_defencoding = 'UTF-8';
It looks like there's a slight omission in the NuSOAP library... it assumes that the content headers MUST be "text/xml", so if your client is attempting to connect to a service that outputs application/soap+xml headers, you'll end up with errors like:
Response not of type text/xml: application/soap+xml; charset=utf-8
To test this, you may benefit from the following little function pattern, which I used to login to a SOAP service. Remember, print out the client object! You may not actually get a result to look at!
require_once('path/to/downloaded/libraries/nusoap.php');
var $endpoint = 'https://somedomain.com/path/to/soap/server/Login';
var $client; // the soapclient object
function SOAP_Login()
{
$this->client = new soapclient($this->endpoint);
$err = $this->client->getError();
if ($err)
{
// Display the error
echo '<p><b>SOAP Constructor error: ' . $err . '</b></p>';
exit;
// At this point, you know the call that follows will fail
}
$params = array(
'some' => 'thing.. depends on what the WSDL expects'
);
$result = $this->client->call('someFunction', $params);
print_r($result); // Without the fix, this prints nothing (i.e. false) !!!
print_r($this->client); // Instead, look at the state of the client object, specifically error_str and debug_str
}
When I printed my $result, I got nothing, but when I printed out the $client object, I could see that there were errors.
The little hack I implemented was in the nusoap.php file, around line 7500. Look for this if-statement:
if (!strstr($headers['content-type'], 'text/xml')) {
$this->setError('Response not of type text/xml: ' . $headers['content-type']);
return false;
}
And change it to this:
if (!strstr($headers['content-type'], 'text/xml') && !strstr($headers['content-type'], 'application/soap+xml') ) {
$this->setError('Response not of type text/xml: ' . $headers['content-type']);
return false;
}
All this does is it lets NuSOAP handle responses that issue an "application/soap+xml" header (which is a valid xml header).
I was stuck on this as well.
The secret is in the web.config
Change wsHttpBinding to basicHttpBinding
Like so:
<endpoint address="" binding="basicHttpBinding" contract="YourProject.View.Whatever.IYourService">
Hope that helps!
/Erik
This worked for me:
$client = new nusoap_client($params);
$client->soap_defencoding = 'UTF-8';
The answer that is ticked as correct is not for NUSOAP therefore not the appropriate answer.