SOAP Request return a null response - php

I am not familiar with SOAP webservices but, i need consume one for a project.
Acording the specification, the webservice has 3 (three) methods:
Enviar
Consultar
Validar
So, i use an instance of SoapClient:
# http://webservices.sathomologa.sef.sc.gov.br/wsDfeSiv/Recepcao.asmx?WSDL
$this->client = new SoapClient(static::SERVICE_WSDL, [
'exceptions' => 1,
'trace' => 1
]);
I think that, i can't use the "auto mapped" methods because i need sign the XML data.
So, i use the SoapClient::__doRequest method:
# Enviar
$this->operation = $operation;
# http://tempuri.org/Enviar
$action = 'http://tempuri.org/' . $operation;
# http://webservices.sathomologa.sef.sc.gov.br/wsDfeSiv/Recepcao.asmx
$location = static::SERVICE_URL;
$v = SOAP_1_1;
$data = $this->xml->saveXML();
try {
$this->client->__doRequest($data, $location, $action, $v);
var_dump($this->client->__getLastResponse());
} catch(SoapFault $e) {
var_dump($e);
}
My problem is that, the SoapClient::__getLastResponse() always return null. Any ideas ?

Related

Using Symfony HTTP client with try and catch

I am using Symfony HTTP client in an event subscriber and I set timeout to 0.1. The problem is I want the code to skip the HTTP request if took more than 0.1 second and it timedout and meanwhile no error/exception needs to be thrown. I tried try and catch but it does not work.
public function checkToken()
{
try {
$response = $this->client->request('POST','url/of/api', [
'json' => ['token' => $_ENV['USER_TOKEN']],
'timeout' => 0.1,
]);
}catch (\Exception $e){
}
}
Why it can not be handled via try and catch?
Its because responses are lazy in Symfony.
As this document mentioned:
https://symfony.com/doc/current/http_client.html#handling-exceptions
A solution is to call getContent method in try section.
private $client;
private $user_data;
public function __construct(HttpClientInterface $client)
{
$this->client = $client;
}
public function checkToken(RequestEvent $event)
{
try {
$response = $this->client->request('POST', 'url/of/api', [
'json' => ['token' => $_ENV['USER_TOKEN']],
'timeout' => 0.1,
]);
$this->user_data = $response->getContent();
} catch (\Exception $e) {
echo $e->getMessage();
}
}

Build correct SOAP Request Header (PHP)

I have to do requets to a SOAP API with PHP and I need the following SOAP-Header structure:
<soapenv:Header>
<ver:authentication>
<pw>xxx</pw>
<user>xxx</user>
</ver:authentication>
</soapenv:Header>
How can I build this header?
I tried
$auth = [
"ver:authentication" => [
"pw" => $this->pw,
"user" => $this->user
]
];
$options = [];
$options["trace"] = TRUE;
$options["cache_wsdl"] = WSDL_CACHE_NONE;
$options["compression"] = SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP;
$client = new SoapClient("www.my-url.com/wsdl", $options);
$header = new SoapHeader("www.my-url.com", "authentication", $auth, false);
$client->__setSoapHeaders($header);
but it does not work. The respons is "failure" which I get, when the header structure is incorrect...
please help
the solution could be object driven. In the following code an example is given. Please keep in mind, that the following code is not testet.
class Authentication
{
protected $user;
protected $pw;
public function getUser() : ?string
{
return $this->user;
}
public function setUser(string $user) : Authentication
{
$this->user = $user;
return $this;
}
public function getPw() : string
{
return $this->pw;
}
public function setPw(string $pw) : Authentication
{
$this->pw = $pw;
return $this;
}
}
The above shown class is a simple entity, which contains two properties $user fpr the username and $pw for the password. Further it contains the getter and setter functions for retrieving or setting the values for the two properties.
For the next step just fill the class with data and store it in a SoapVar object.
$authentication = (new Authentication())
->setUser('Username')
->setPw('YourEncodedPassword');
$soapEncodedObject = new \SoapVar(
$authentication,
SOAP_ENC_OBJECT,
null,
null,
'authentication',
'http://www.example.com/namespace'
);
As you can see above, your authentication class will be stored as soap var object. It is encoded as soap object. The only thing you have to do is setting the namespace for this object. In your given example it is ver:. With this namespace prefix somewhere in your wsdl file a namespace is noted. You have to find out this namespace url and just replace the example url http://www.example.com/namespace with the right url noted in your wsdl.
The next step is setting this as soap header. That 's quite simple.
try {
$client = new SoapClient('http://www.example.com/?wsdl', [
'trace' => true,
'exception' => true,
'cache_wsdl' => WSDL_CACHE_NONE,
'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP,
]);
// set the soap header
$header = new SoapHeader('http://www.example.com/namespace', 'authentication', $authentication, false);
$client->setSoapHeaders($header);
// send the request
$result = $client->someWsdlFunction($params);
} catch (SoapFault $e) {
echo "<pre>";
var_dump($e);
echo "</pre>";
if ($client) {
echo "<pre>";
var_dump($client->__getLastRequest());
echo "</pre>";
echo "<pre>";
var_dump($client->__getLastResponse());
echo "</pre>";
}
}
As you can see it 's a bit different from your given example. Instead of an array it 's the soap encoded authentication object, that is given to the soap header class. For failure purposes there is a try/catch block around your soap client. In that case you can identify the error and if the client was initiated correctly, you can also see the last request and last response in xml.
I hope, that I helped you. ;)
I would strongly advise you 2 things:
Use a WSDL to PHP generator in order to properly construct your request. In addition, it will ease you the response handling. Everything is then using the OOP which is much better. Take a look to the PackageGenerator project.
Use the WsSecurity project in order to easily add your dedicated SoapHeader without wondering how to construct it neither.

Mock response and use history middleware at the same time in Guzzle

Is there any way to mock response and request in Guzzle?
I have a class which sends some request and I want to test.
In Guzzle doc I found a way how can I mock response and request separately. But how can I combine them?
Because, If use history stack, guzzle trying to send a real request.
And visa verse, when I mock response handler can't test request.
class MyClass {
public function __construct($guzzleClient) {
$this->client = $guzzleClient;
}
public function registerUser($name, $lang)
{
$body = ['name' => $name, 'lang' = $lang, 'state' => 'online'];
$response = $this->sendRequest('PUT', '/users', ['body' => $body];
return $response->getStatusCode() == 201;
}
protected function sendRequest($method, $resource, array $options = [])
{
try {
$response = $this->client->request($method, $resource, $options);
} catch (BadResponseException $e) {
$response = $e->getResponse();
}
$this->response = $response;
return $response;
}
}
Test:
class MyClassTest {
//....
public function testRegisterUser()
{
$guzzleMock = new \GuzzleHttp\Handler\MockHandler([
new \GuzzleHttp\Psr7\Response(201, [], 'user created response'),
]);
$guzzleClient = new \GuzzleHttp\Client(['handler' => $guzzleMock]);
$myClass = new MyClass($guzzleClient);
/**
* But how can I check that request contains all fields that I put in the body? Or if I add some extra header?
*/
$this->assertTrue($myClass->registerUser('John Doe', 'en'));
}
//...
}
#Alex Blex was very close.
Solution:
$container = [];
$history = \GuzzleHttp\Middleware::history($container);
$guzzleMock = new \GuzzleHttp\Handler\MockHandler([
new \GuzzleHttp\Psr7\Response(201, [], 'user created response'),
]);
$stack = \GuzzleHttp\HandlerStack::create($guzzleMock);
$stack->push($history);
$guzzleClient = new \GuzzleHttp\Client(['handler' => $stack]);
First of all, you don't mock requests. The requests are the real ones you are going to use in production. The mock handler is actually a stack, so you can push multiple handlers there:
$container = [];
$history = \GuzzleHttp\Middleware::history($container);
$stack = \GuzzleHttp\Handler\MockHandler::createWithMiddleware([
new \GuzzleHttp\Psr7\Response(201, [], 'user created response'),
]);
$stack->push($history);
$guzzleClient = new \GuzzleHttp\Client(['handler' => $stack]);
After you run your tests, $container will have all transactions for you to assert. In your particular test - a single transaction. You are interested in $container[0]['request'], since $container[0]['response'] will contain your canned response, so there is nothing to assert really.

SOAP action error when using soapClient

i am getting this error when trying to make a soap call.
The SOAP action specified on the message, '', does not match the HTTP SOAP Action.
When i call $service->SearchTouristItems($sti); (this function is further below) i get the above error and i have no idea why.
The below is the code i am using.
// i used http://www.urdalen.no/wsdl2php/ to create TCS2Service which extends SoapClient
$service = new TCS2Service() ;
$sd = new ServiceDescriptor;
$sd->UniqueIdentifier = 'xxxxxxxxx-xxxxx-xxxx-xxxxx-xxxxxx';
$stic = new SearchTouristItemCriteria;
$stic->SearchString = array ('dublin') ;
$sti = new SearchTouristItems;
$sti->searchTouristItemCriteria = $sd;
$sti->serviceDescriptor = $stic;
$result = $service->SearchTouristItems($sti);
echo "<pre>";
print_r($result);
echo "</pre>";
SearchTouristItems looks like this
/**
*
*
* #param SearchTouristItems $parameters
* #return SearchTouristItemsResponse
*/
public function SearchTouristItems(SearchTouristItems $parameters) {
return $this->__soapCall('SearchTouristItems', array($parameters), array(
'uri' => 'http://tempuri.org/',
'soapaction' => ''
)
);
}
this is the initilization of the client
public function TCS2Service($wsdl = "http://www.example.com/services/TCS2Service.svc", $options = array( 'soap_version' => SOAP_1_2,
'exceptions' => true,
'trace' => 1,
'cache_wsdl' => WSDL_CACHE_NONE,)) {
foreach(self::$classmap as $key => $value) {
if(!isset($options['classmap'][$key])) {
$options['classmap'][$key] = $value;
}
}
parent::__construct($wsdl, $options);
}
Not sure though but what is the value of 'soapaction' => '' in your code is replaced with the provided parameter. I do not have that experience calling web services with PHP so just gave it a thought.
I think your ws-addressing is not turned on. Please turn on the ws-
addressing and check again.
What I would do :
check if the SOAP action is well defined in the WSDL: look for address location="
try another WSDL to php converter
send the WSDL url so I can try it by my side

How to retrieve full Zend_Http_Client GET URI?

I have something like that:
$client = new Zend_Http_Client('http://www.site.com');
$client->setParameterGet(array(
'platform' => $platform,
'clientId' => $clientId,
'deploymentId' => $deploymentId,
));
try {
$response = $client->request();
...
This would generate a request similar to 'http://www.site.com/?plataform=..?clientid?..'.
Is there a way I could retrieve this full URL generated by this GET request?
Kind regards,
Surprisingly enough there is no direct method for getting full request string.
BUT
After the request is done you could check $client->getLastRequest
().
If you need to know what the ?plataform=..?clientid? part of the
request is there is a trick.
function getClientUrl (Zend_Http_Client $client)
{
try
{
$c = clone $client;
/*
* Assume there is nothing on 80 port.
*/
$c->setUri ('http://127.0.0.1');
$c->getAdapter ()
->setConfig (array (
'timeout' => 0
));
$c->request ();
}
catch (Exception $e)
{
$string = $c->getLastRequest ();
$string = substr ($string, 4, strpos ($string, "HTTP/1.1\r\n") - 5);
}
return $client->getUri (true) . $string;
}
$client = new Zend_Http_Client ('http://yahoo.com');
$client->setParameterGet ('q', 'search string');
echo getClientUrl ($client);

Categories