Context:
I've been working on figuring out how to make this work for a while, and I simply don't understand why Guzzle isn't working for this particular request. The same initialization and request structure works in some basic unit tests I have, but when it comes to API to API communication, Guzzle just is not cooperating.
Problem:
What I mean by that is, it's not including the headers I'm setting in the $headers array, and the request body is empty.
Desired result:
To confirm this is an issue with Guzzle, I've written out the request with typical cURL syntax, and the request goes through fine. I just need some guidance on how to make this work with Guzzle, as I like the abstraction Guzzle offers over verbose cURL requests.
Working cURL request:
$headers = array(
'Authorization: Bearer '.$sharedSecret,
'Content-Type: application/x-www-form-urlencoded',
'Accept: application/json',
'Content-Length: '.strlen($loginDetails),
);
$curlOptions = array(
CURLOPT_URL => API_URL.'member/SessionManager',
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => FALSE,
CURLOPT_HEADER => FALSE,
CURLOPT_FOLLOWLOCATION => FALSE,
CURLOPT_ENCODING => "",
CURLOPT_USERAGENT => "PORTAL",
CURLOPT_AUTOREFERER => TRUE,
CURLOPT_CONNECTTIMEOUT => 120,
CURLOPT_TIMEOUT => 120,
CURLOPT_MAXREDIRS => 10,
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => $loginDetails,
CURLOPT_SSL_VERIFYHOST => FALSE,
CURLOPT_SSL_VERIFYPEER => FALSE,
CURLOPT_VERBOSE => FALSE
);
try {
$ch = curl_init();
curl_setopt_array($ch,$curlOptions);
$content = curl_exec($ch);
$err = curl_errno($ch);
$errmsg = curl_error($ch);
$response = curl_getinfo($ch);
curl_close($ch);
if ($content === FALSE) {
throw new Exception(curl_error($ch), curl_errno($ch));
} else {
return true;
}
} catch(Exception $e) {
trigger_error(sprintf('Curl failed with error #%d: %s', $e->getCode(), $e->getMessage()), E_USER_ERROR);
}
The Guzzle client is initialized in a global file that creates a container which stores various objects we use throughout the application. I'm including it in case I missed something vital that isn't in Guzzle's documentation.
Guzzle initialization:
$container['client'] = function ($c) {
return new \GuzzleHttp\Client([
'base_uri' => API_URL,
'timeout' => 30.0,
'allow_redirects' => true,
'verify' => false,
'verbose' => true,
[
'headers' => [
'Authorization' => 'Bearer '.$sharedSecret,
]
],
]);
};
Non-working Guzzle Request:
$headers = array(
'Authorization' => 'Bearer '.$sharedSecret,
'Content-Type' => 'application/x-www-form-urlencoded',
'Accept' => 'application/json',
'Content-Length'=> strlen($loginDetails),
);
try {
$response = $this->client->post('/api/member/SessionManager',
['debug' => true],
['headers' => $headers],
['body' => $loginDetails]
);
if($response) {
$this->handleResponse($response);
}
} catch (GuzzleHttp\Exception\ClientException $e) {
$response->getResponse();
$responseBodyAsString = $response->getBody()->getContents();
}
Whether I remove the headers array in the Guzzle initialization or not, it doesn't matter. The Authorization header is nowhere to be found in the request (confirmed with tcpdump, Wireshark, and receiving API error logging), and Guzzle's debug output shows no indication of my headers, nor my request body being anywhere in the request.
I'm stumped as to why this isn't working, and would very much like to understand. I can go the route of not using Guzzle, but would really prefer to due to brevity. Any input would be greatly appreciated.
In cURL request, the API URL being called is
API_URL.'member/SessionManager'
While in Guzzle request, the API URL being called has extra text "/api/"
(assuming API_URL is defined same in both cases)
new \GuzzleHttp\Client([
'base_uri' => API_URL,
...
$this->client->post('/api/member/SessionManager',
Appreciate that the question is old, but I thought I'd share my experience as I also struggled with this. The equivalent of:
CURLOPT_POSTFIELDS => $loginDetails,
in Guzzle is:
"query" => $loginDetails
In addition, I have found that if the base_uri does not end with a /, Guzzle will misunderstand it.
With all that in mind, your POST request should be as follows:
$response = $this->client->post('api/member/SessionManager', // Removed the first / as it's part of the base_uri now
['debug' => true],
['headers' => $headers],
['query' => $loginDetails] // Replaced "body" with "query"
);
Related
How to pass client certificate (2 files .key and .pem) through http client request?
I need to include those files in the below http post request to be able to talk with the server.
$response = Http::post('https://domainname.com/api/client/session', array('xxx' => array('xx' => 'xxxx')));
I am able to do it using phpCurl like below:
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => $type,
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => $header,
CURLOPT_SSLKEY => $pemPath,
CURLOPT_SSLCERT => $crtPath,
// CURLOPT_NOSIGNAL => 1
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
echo (json_encode($response));
echo ("\n\n");
if ($err) {
return ["success" => false, "message" => $err];
} else {
return ["success" => true, "data" => json_decode($response)];
}
But I need to do it using Http Client for many other purposes. Any suggestion?
You can use Guzzle options provided by http client
$response = Http::withOptions([
'ssl_key' => ['/path/to/cert.pem', 'password.key']
])->post('https://domainname.com/api/client/session');
It would be same as using ssl_key request option for guzzle http client instance directly.
use GuzzleHttp\Client;
$client = new Client();
$client->request('POST', 'https://domainname.com/api/client/session', [
'ssl_key' => ['/path/to/cert.pem', 'password.key']
]);
In a PHP OAuth Implementation, there is a function as below:
/**
* Processes POST requests to /oauth/token.
*/
public function token(ServerRequestInterface $request) {
// Extract the grant type from the request body.
$body = $request->getParsedBody();
$grant_type_id = !empty($body['grant_type']) ? $body['grant_type'] : 'implicit';
/*.. CODE TRIMMED ..*/
catch (OAuthServerException $exception) {
watchdog_exception('simple_oauth', $exception);
$response = $exception->generateHttpResponse(new Response());
}
return $response;
}
I think its an instance of:
Psr\Http\Message\ServerRequestInterface;
I want to call this function, and pass the body params to this function from another function. Something as below:
public function call_token(){
//Something like this??
$request = new ServerRequestInterface();
$request->setUrl('https://some.url/oauth/token');
$request->setMethod("POST");
$request->setHeader(array(
'Content-Type' => 'application/x-www-form-urlencoded',
));
$request->addPostParameter(array(
'grant_type' => 'password',
'client_id' => '828472a8-f2c5-4e79-a158-ab041d3b313a',
'client_secret' => 'secret',
'username' => 'admin',
'password' => '123'
));
$response = $token($request);
}
I am not able to figure out how we can call this function.
This is the full source code where the token code is implemented. I am trying to call this function from another class.
Below is a working CURL request of how requests are made to this endpoint. I however want to emulate the request from within the application.
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://some.url/oauth/token',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => 'grant_type=password&client_id=828472a8-f2c5-4e79-a158-ab041d3b313a&client_secret=secret&username=admin&password=123',
CURLOPT_HTTPHEADER => array(
'Content-Type: application/x-www-form-urlencoded',
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
When using either dynamically created JWT tokens, or even hard-copying the one provided from the App Marketplace for my app, my API requests always fail due to an ‘invalid access token’.
I am currently working on the Meetings endpoint, specifically trying to create a meeting. The endpoint is: https://eu01api-www4local.zoom.us/v2/users/me/meetings (using the GDPR compliant EU base URL).
My cURL request looks like this:
$body = json_encode($body);
$arr = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => [
"authorization: Bearer " . $this->generate_JWT(),
"content-type: application/json"
],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body
];
$ch = curl_init();
curl_setopt_array($ch, $arr);
$response = curl_exec($ch);
$err = curl_error($ch);
curl_close($ch);
(CURLOPT_SSL_VERIFYPEER is set to false here, as I'm testing from localhost.)
The generate_JWT function looks like this:
private function generate_JWT()
{
$payload = [
"iss" => self::ZOOM_API_SECRET,
"exp" => time() + $this->timeout,
];
return JWT::encode($payload, self::ZOOM_API_KEY, 'HS256');
}
... using the Firebase\JWT\JWT class for encoding.
The 'app' itself is activated and live on the account, set as JWT type, with no intent to publish, and is 'Account level'.
Any help appreciated.
The endpoint described in the Documentation for GDPR compliant EU users is not, in fact, operable - at least for this type of work. The original .us base URL must be used.
Whenever I use Postman to make a soap post request, I get back the desired data. Using Guzzle, no data is returned. Im new to SOAP and using the resources online to go along.
I have omitted the variables for ease of read.
$xml = (
'<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:app="<ACTION-URL>">
<soapenv:Body>
<app:' . $functionName . '>
<request>'
. $requestBody .
'<authentication>
<username>'. $authObject->username .'</username>
<password>'. $authObject->password .'</password>
<user_id>'. $authObject->userId .'</user_id>
<dealer_id>'. $authObject->clientBranchId .'</dealer_id>
</authentication>
</request>
</app:' . $functionName . '>
</soapenv:Body>
</soapenv:Envelope>'
);
private function makeSOAPRequest($xml)
{
$client = new \GuzzleHttp\Client();
$options = [
'headers' => [
'Content-Type' => 'text/xml; charset=utf-8'
],
'body' => $xml,
'Authenticate' => [env('USERNAME'), env('PASSWORD')]
];
$url = env('ROSETTA_API');
$promise = $client->requestAsync('POST', $url, $options);
$response = $promise->wait();
$xml = simplexml_load_string($response->getBody(),'SimpleXMLElement',LIBXML_NOCDATA);
if ($xml) {
$json = json_encode($xml);
return json_decode($json, true);
}
return false;
}
Calling makeSOAPRequest(..), I get back false. Using the same xml data in Postman, data is returned. Is there anything I've missed in the request header?
Edit to use cURL
// Copied from Postman:
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://<URL>",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => $xml,
CURLOPT_HTTPHEADER => array(
"Cookie: PHPSESSID=6419f1d96025a4a2c4d454de33fc6820"
),
));
$response = curl_exec($curl);
curl_close($curl);
return $response; // xml data in string format.
cUrl does work but not Guzzle. How to return as json? When I use simplexml_load_string, it's empty.
Try to use below parameters in your curl:
CURLOPT_SSL_VERIFYPEER => 1
CURLOPT_POST => true
// IF NEEDED
CURLOPT_HTTPAUTH => CURLAUTH_ANY
// IF NEEDED
CURLOPT_USERPWD => $soapUser.":".$soapPassword)
For Checking Error After curl_exec and before curl_close
if( curl_errno($ch) ){
print_r(curl_error($curl));
}
In add these headers in your header if needed:
"accept" => "*/*",
// IF NEEDED
"accept-encoding" => "gzip, deflate"
Add your request code in try catch block:
try{
//request code
}
catch(Exception $e){
print_r($e->getCode());
print_r($e->getMessage());
}
I read from here : http://www.phplab.info/categories/laravel/consume-external-api-from-laravel-5-using-guzzle-http-client
I try like this :
...
use GuzzleHttp\Client as GuzzleHttpClient;
use GuzzleHttp\Exception\RequestException;
...
public function testApi()
{
try {
$client = new GuzzleHttpClient();
$apiRequest = $client->request('POST', 'https://myshop/api/auth/login', [
// 'query' => ['plain' => 'Ab1L853Z24N'],
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'auth' => ['test#gmail.com', '1234'], //If authentication required
// 'debug' => true //If needed to debug
]);
$content = json_decode($apiRequest->getBody()->getContents());
dd($content);
} catch (RequestException $re) {
//For handling exception
}
}
When executed, the result is null
How can I get the response?
I try in postman, it success get response
But I try use guzzle, it failed
Update :
I check on the postman, the result works
I try click button code on the postman
Then I select php curl and I copy it, the result like this :
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://myshop/api/auth/login",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"email\"\r\n\r\ntest#gmail.com\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"password\"\r\n\r\n1234\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--",
CURLOPT_HTTPHEADER => array(
"cache-control: no-cache",
"content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW",
"postman-token: 1122334455-abcd-edde-aabe-adaddddddddd"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
If it use curl php, the code like that
How can I get the response if it use guzzle?
I see at least one syntax mistake. The third argument of the request() method should look like this:
$requestContent = [
'headers' = [],
'json' = []
];
In your case it could be:
public function testApi()
{
$requestContent = [
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json'
],
'json' => [
'email' => 'test#gmail.com',
'password' => '1234',
// 'debug' => true
]
];
try {
$client = new GuzzleHttpClient();
$apiRequest = $client->request('POST', 'https://myshop/api/auth/login', $requestContent);
$response = json_decode($apiRequest->getBody());
dd($response);
} catch (RequestException $re) {
// For handling exception.
}
}
There are other parameters instead of json for your data, for example form_params. I suggest you take a look at the Guzzle documentation.