I you receive a http response 403 Forbidden
then you either not allowed to communicate with the url you request (check if you have all api keys etc generated) and/or your request is incomplete/wrong.
You may exclude Guzzle as a reason of the failure and narrow the possible causes by doing the same request with other tools. If you succeed then it means the problem is with the Guzzle, but if you don't then one of the problems described above is the real issue.
To do request with PHP's native funcion file_get_contents() use this code:
<?php
# display as a plain text instead of html, just for test
header('Content-Type: text/plain');
# full url including '/configs'
$url = 'https://www.example.com/configs/';
$headers = [
"X-Api-Id" => "id",
"X-Api-Secret" => "secret",
"X-Api-Key" => "key"
];
# optional query str
// $queryArr = [
// 'key' => 'value'
// ];
# ----------- prepare request -----------------
if (!empty($queryArr)) {
$url .= '?' . http_build_query($queryArr);
}
$opts = [];
$opts['http']['method'] = "GET";
if (!empty($headers)) {
$headersStr = '';
foreach ($headers as $header => $value) {
$headersStr .= $header . ': ' . $value . "\r\n";
}
$opts['http']['header'] = $headersStr;
$context = stream_context_create($opts);
}
# ----------- prepare request end ------------
if (isset($context)) {
$response = file_get_contents($url, false, $context);
} else {
$response = file_get_contents($url);
}
echo $response;
I checked that method with my own service and it works, all headers are send.
But if you don't believe me you can do the same by using cURL.
Type in the command line:
curl --insecure -L -H -S -v --connect-timeout 5 -H "X-Api-Id: id" -H "X-Api-Secret: secret" -H "X-Api-Key: key" "https://www.example.com/configs/"
you may need instead of curl
at the beginning use the full path to the executable like C:\Downloads\curl.exe
at Windows.
The options I used:
--insecure Allow insecure server connections when using SSL
-L, --location Follow redirects
-H, --header <header/@file> Pass custom header(s) to server
-S, --show-error Show error even when -s is used
-v A line starting with '>' means "header data" sent by curl, '<' means "header data"
received by curl that is hidden in normal cases, and a line starting with '*' means additional
info provided by curl.
--connect-timeout <seconds> Maximum time allowed for connection
You should firstly try the command line above without:
--insecure Allow insecure server connections when using SSL
but if you have a problem on Windows with checking certificates this is one of the workarounds (not recommended), it is better to fix that problem
for the command line curl certificate check this link, see section Certificate Verification point 4.
Additionally how to fix certificate problem with php
If you run the command line curl example as above you should get something like that in the output:
* Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to www.example.com (xxx.xxx.xxx.xxx) port 80 (#0)
> GET /configs/ HTTP/1.1
> Host: www.example.com
> User-Agent: curl/7.58.0
> Accept: */*
> X-Api-Id: id
> X-Api-Secret: secret
> X-Api-Key: key
>
< HTTP/1.1 200 OK
< Connection: Keep-Alive
< Keep-Alive: timeout=5, max=100
(...)
where as previously mentioned:
-v A line starting with '>' means "header data" sent by curl, '<' means "header data"
So you can be sure and have a proof that the headers were send:
> X-Api-Id: id
> X-Api-Secret: secret
> X-Api-Key: key
There are some additional headers like:
> Accept: */*
And if you want to receive a response body as json
you probably should also set this header too by adding to the command line:
-H "Accept: application/json"
but check that with the documentation of the api you request.
Once you confirm everything is working as it should by file_get_contents
or cURL
command line you can check everything with the Guzzle
. Before you do that check if your php has curl
by the command line:
php -m
you will see all the extensions in the php installed and there should be an extension named curl
Guzzle example to do a request with headers and optional query string.
I used guzzle installed by this command:
composer require "guzzlehttp/guzzle:^7.2"
Code using Guzzle:
<?php
require_once "../vendor/autoload.php";
# display as a plain text instead of html, just for test
header('Content-Type: text/plain');
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\RequestOptions;
try {
$headers = [
"X-Api-Id" => "id",
"X-Api-Secret" => "secret",
"X-Api-Key" => "key"
];
# optional query str
// $queryArr = [
// 'key' => 'value'
// ];
$method = 'GET';
$scheme = 'https';
$baseUrl = 'www.example.com';
$urlDir = '/configs/';
$baseUri = strtolower($scheme) . '://' . $baseUrl;
$clientConfigArr = [
'base_uri' => $baseUri,
RequestOptions::TIMEOUT => 5.0, // Timeout if a server does not return a response in 5 seconds.
RequestOptions::VERIFY => false, // Disable validation entirely (don't do this!).
RequestOptions::DEBUG => true,
];
# set method to default value if not set
if (empty(trim($method))) {
$method = 'GET';
}
# set location to / if not set
if (empty(trim($urlDir))) {
$urlDir = '/';
}
# add query string if query arr not empty
if (!empty($queryArr)) {
$urlDir .= '?' . http_build_query($queryArr);
}
# if headers are set
if (!empty($headers)) {
# create request with headers
$request = new Request(strtoupper(trim($method)), $urlDir, $headers);
} else {
# create request without headers
$request = new Request(strtoupper(trim($method)), $urlDir);
}
# create client and send a the requrest
$client = new Client($clientConfigArr);
$response = $client->sendRequest($request);
} catch (\GuzzleHttp\Exception\TransferException $e) {
$msg = ' ';
$msg .= \PHP_EOL;
$msg .= '[BAD] [Transfer ERR: ' . $e->getCode() . ']' . \PHP_EOL;
$msg .= 'at ' . $e->getFile() . ':' . $e->getLine() . \PHP_EOL;
if (!empty($response) && is_object($response) && method_exists($response, 'getBody')) {
$msg .= '[Response Body] ' . (string) $response->getBody() . \PHP_EOL;
}
echo "$msg";
exit(1);
} catch (\Throwable $e) {
$msg = '';
$msg .= \PHP_EOL;
$msg .= '[BAD] [ERROR Throwable]' . \PHP_EOL;
$msg .= '[Message] ' . $e->getMessage() . \PHP_EOL;
$msg .= 'at ' . $e->getFile() . ':' . $e->getLine() . \PHP_EOL;
echo "$msg";
exit(1);
}
echo '[OK] [SUCCESS]' . \PHP_EOL . \PHP_EOL;
echo $response->getBody() . \PHP_EOL;
Note that using:
RequestOptions::VERIFY => false
is the same as using --insecure
for cURL command line and should be avoided, better fix the problem with the SSL certificates by following the instruction previously given. Having that problem fixed you may remove the RequestOptions::VERIFY => false
from the $clientConfigArr
Guzzle usefull links:
Request
Request Options
How to check if a request or response has a specific header.
Client
Response
Exceptions
It is possible that you should not do the GET
request but POST
instead as Martin mentioned. If you want to make a request with different method then:
Request Methods
For the POST
method you will need most likely send a body as well, you can create a POST
Request
and attach the body this way:
use GuzzleHttp\Psr7\Request;
$headers = ['X-Foo' => 'Bar'];
$body = 'hello!';
$request = new Request('POST', 'http://httpbin.org/post', $headers, $body);
# and send request (as in the Guzzle example)
$client = new Client($clientConfigArr);
$response = $client->sendRequest($request);
check with the api documentation if you need to pass some body content (perhaps your token) if you are going to do the POST
request (or any other method)
A side note:
In the Guzzle Code example instead of require I used require_once because:
if the code from a file has already been included, it will not be included again.
source
Therefore you can use in may files as many times as you want:
require_once "../vendor/autoload.php";
and everything will work fine, in contrary to the require
without once
that will very often make you duplicate declaration errors.
For having windows like backslashes you may use DIRECTORY_SEPARATOR and DIR as a current dir. Using them both like this:
require_once __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
Just in case you would like to know.
$client = new GuzzleHttp\Client(['headers' => ['X-Foo' => 'Bar']]);
and after send the request$this->client->request("GET", "/configs/");
? - gaidyjg