GuzzleHTTP headers 设置不正确
GuzzleHTTP headers not set properly
我正在尝试使用我的密钥向 api 发送 GET 请求。我的密钥需要在 header 中。我已将我的密钥保存在数组 $api
中并将它们提供给 Guzzle.
当我发送 GET 请求时,我收到了 403 Forbidden
响应,我最好的猜测是 header 实际上并没有被设置。
require "../vendor/autoload.php";
use GuzzleHttp\Client;
$api = array(
"X-Api-Id" => "id",
"X-Api-Secret" => "secret",
"X-Api-Key" => "key"
);
$client = new GuzzleHttp\Client([
"base_uri" => "example.com",
"timeout" => 5
]);
$response = $this->client->request("GET", "/configs/", ["verify" => false, "headers" => $api]);
var_dump($response);
错误
Fatal error: Uncaught GuzzleHttp\Exception\ClientException: Client error: GET https://api.onetap.com/configs/
resulted in a 403 Forbidden
response: <!--[if IE 7]> <html class (truncated...) in C:\scaryconfigscc\vendor\guzzlehttp\guzzle\src\Exception\RequestException.php on line 113
和documentation没什么区别,另外还有默认的headers:
try {
$response = $client->request('GET', '/configs', [
'headers' => [
'User-Agent' => 'testing/1.0',
'Accept' => 'application/json',
'X-Api-Id' => 'id',
'X-Api-Secret' => 'secret',
'X-Api-Key' => 'key'
]
]);
} catch (ClientException $e) {
echo 'Caught exception: ' . $e->getMessage() . "\n";
}
如果它不起作用 - 凭据错误或者它可能需要方法 POST。
您可以尝试以下方法:
- 使用
$request->getHeader()
检索正在发送的所有 headers 并检查是否设置了所需的 headers。
- 检查
$response->getHeader()
并验证 api 是否正在寻找设置 cookie。如果实际设置了 cookie,则需要设置 cookiejar (https://docs.guzzlephp.org/en/stable/quickstart.html#cookies)。
如果以上都不能解决问题,那就麻烦了。从这里开始更多的是反复试验。您可以尝试以下一项或全部:
- 更改用户代理
- 可能是 api 阻止了您的 IP(因此您需要在不同的服务器或 IP 上尝试)
- 他们可能会检测到您作为违规用户的某些使用情况,并且可能会通过他们的防火墙或在应用程序级别阻止该请求。触发规则的是你和他们自己想的。
如果仍然无效,您需要联系他们的支持人员或分享更多详细信息,然后其他人才能为您提供帮助。
如果你收到一个 http 响应 403 Forbidden
那么你要么不允许与你请求的 url 通信(检查你是否生成了所有 api 密钥等)and/or 你的请求是 incomplete/wrong.
您可以排除 Guzzle 作为失败的原因,并通过使用其他工具执行相同的请求来缩小可能的原因。如果成功,则表示问题出在 Guzzle,但如果不成功,则上述问题之一才是真正的问题。
要使用 PHP 的本机函数 file_get_contents() 进行请求,请使用此代码:
<?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;
我用自己的服务检查了该方法,它有效,所有 header 都已发送。
但是如果你不相信我,你可以使用 cURL 来做同样的事情。
在命令行中输入:
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/"
您可能需要在开头而不是 curl
使用可执行文件的完整路径,例如 C:\Downloads\curl.exe
at Windows.
我使用的选项:
--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
您应该首先尝试上面的命令行,而不是:
--insecure Allow insecure server connections when using SSL
但是,如果您在 Windows 上遇到证书检查问题,这是解决方法之一(不推荐),最好解决该问题
对于命令行curl证书check this link, see section Certificate Verification point 4.
Additionally how to fix certificate problem with php
如果你 运行 上面的命令行 curl 示例你应该在输出中得到类似的东西:
* 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
(...)
如前所述:
-v A line starting with '>' means "header data" sent by curl, '<' means "header data"
因此您可以确定并证明 header 已发送:
> X-Api-Id: id
> X-Api-Secret: secret
> X-Api-Key: key
还有一些额外的 header,例如:
> Accept: */*
如果你想收到一个响应body作为json
你可能也应该通过添加到命令行来设置这个header:
-H "Accept: application/json"
但请检查您所请求的 api 的文档。
通过 file_get_contents
或 cURL
命令行确认一切正常后,您可以使用 Guzzle
检查一切。在你这样做之前,通过命令行检查你的php是否有curl
:
php -m
您将看到 php 中的所有扩展都已安装,并且应该有一个名为 curl
的扩展
Guzzle 使用 header 和可选查询字符串执行请求的示例。
我使用了通过此命令安装的 guzzle:
composer require "guzzlehttp/guzzle:^7.2"
代码使用 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;
注意使用:
RequestOptions::VERIFY => false
与在 cURL 命令行中使用 --insecure
相同,应该避免,按照之前给出的说明更好地解决 SSL 证书的问题。解决该问题后,您可以从 $clientConfigArr
中删除 RequestOptions::VERIFY => false
Guzzle 有用链接:
Request
Request Options
How to check if a request or response has a specific header.
Client
Response
Exceptions
您可能不应该执行 GET
请求,而应执行 POST
请求,正如 Martin 提到的那样。如果您想使用不同的方法发出请求,则:
对于 POST
方法,您很可能还需要发送一个 body,您可以创建一个 POST
Request
并附加 body这样:
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);
如果您要执行 POST
请求(或任何其他方法),如果您需要传递一些 body 内容(可能是您的令牌),请查看 api 文档
旁注:
在 Guzzle 代码示例中而不是 require I used require_once 因为:
if the code from a file has already been included, it will not be included again.
因此,您可以根据需要多次使用 may 文件:
require_once "../vendor/autoload.php";
一切都会正常工作,与没有 once
的 require
相反,后者通常会使您重复声明错误。
为了使 windows 像反斜杠一样,您可以使用 DIRECTORY_SEPARATOR and DIR 作为当前目录。像这样使用它们:
require_once __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
以防万一你想知道。
我正在尝试使用我的密钥向 api 发送 GET 请求。我的密钥需要在 header 中。我已将我的密钥保存在数组 $api
中并将它们提供给 Guzzle.
当我发送 GET 请求时,我收到了 403 Forbidden
响应,我最好的猜测是 header 实际上并没有被设置。
require "../vendor/autoload.php";
use GuzzleHttp\Client;
$api = array(
"X-Api-Id" => "id",
"X-Api-Secret" => "secret",
"X-Api-Key" => "key"
);
$client = new GuzzleHttp\Client([
"base_uri" => "example.com",
"timeout" => 5
]);
$response = $this->client->request("GET", "/configs/", ["verify" => false, "headers" => $api]);
var_dump($response);
错误
Fatal error: Uncaught GuzzleHttp\Exception\ClientException: Client error:
GET https://api.onetap.com/configs/
resulted in a403 Forbidden
response: <!--[if IE 7]> <html class (truncated...) in C:\scaryconfigscc\vendor\guzzlehttp\guzzle\src\Exception\RequestException.php on line 113
和documentation没什么区别,另外还有默认的headers:
try {
$response = $client->request('GET', '/configs', [
'headers' => [
'User-Agent' => 'testing/1.0',
'Accept' => 'application/json',
'X-Api-Id' => 'id',
'X-Api-Secret' => 'secret',
'X-Api-Key' => 'key'
]
]);
} catch (ClientException $e) {
echo 'Caught exception: ' . $e->getMessage() . "\n";
}
如果它不起作用 - 凭据错误或者它可能需要方法 POST。
您可以尝试以下方法:
- 使用
$request->getHeader()
检索正在发送的所有 headers 并检查是否设置了所需的 headers。 - 检查
$response->getHeader()
并验证 api 是否正在寻找设置 cookie。如果实际设置了 cookie,则需要设置 cookiejar (https://docs.guzzlephp.org/en/stable/quickstart.html#cookies)。
如果以上都不能解决问题,那就麻烦了。从这里开始更多的是反复试验。您可以尝试以下一项或全部:
- 更改用户代理
- 可能是 api 阻止了您的 IP(因此您需要在不同的服务器或 IP 上尝试)
- 他们可能会检测到您作为违规用户的某些使用情况,并且可能会通过他们的防火墙或在应用程序级别阻止该请求。触发规则的是你和他们自己想的。
如果仍然无效,您需要联系他们的支持人员或分享更多详细信息,然后其他人才能为您提供帮助。
如果你收到一个 http 响应 403 Forbidden
那么你要么不允许与你请求的 url 通信(检查你是否生成了所有 api 密钥等)and/or 你的请求是 incomplete/wrong.
您可以排除 Guzzle 作为失败的原因,并通过使用其他工具执行相同的请求来缩小可能的原因。如果成功,则表示问题出在 Guzzle,但如果不成功,则上述问题之一才是真正的问题。
要使用 PHP 的本机函数 file_get_contents() 进行请求,请使用此代码:
<?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;
我用自己的服务检查了该方法,它有效,所有 header 都已发送。
但是如果你不相信我,你可以使用 cURL 来做同样的事情。 在命令行中输入:
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/"
您可能需要在开头而不是 curl
使用可执行文件的完整路径,例如 C:\Downloads\curl.exe
at Windows.
我使用的选项:
--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
您应该首先尝试上面的命令行,而不是:
--insecure Allow insecure server connections when using SSL
但是,如果您在 Windows 上遇到证书检查问题,这是解决方法之一(不推荐),最好解决该问题
对于命令行curl证书check this link, see section Certificate Verification point 4.
Additionally how to fix certificate problem with php
如果你 运行 上面的命令行 curl 示例你应该在输出中得到类似的东西:
* 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
(...)
如前所述:
-v A line starting with '>' means "header data" sent by curl, '<' means "header data"
因此您可以确定并证明 header 已发送:
> X-Api-Id: id
> X-Api-Secret: secret
> X-Api-Key: key
还有一些额外的 header,例如:
> Accept: */*
如果你想收到一个响应body作为json
你可能也应该通过添加到命令行来设置这个header:
-H "Accept: application/json"
但请检查您所请求的 api 的文档。
通过 file_get_contents
或 cURL
命令行确认一切正常后,您可以使用 Guzzle
检查一切。在你这样做之前,通过命令行检查你的php是否有curl
:
php -m
您将看到 php 中的所有扩展都已安装,并且应该有一个名为 curl
Guzzle 使用 header 和可选查询字符串执行请求的示例。
我使用了通过此命令安装的 guzzle:
composer require "guzzlehttp/guzzle:^7.2"
代码使用 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;
注意使用:
RequestOptions::VERIFY => false
与在 cURL 命令行中使用 --insecure
相同,应该避免,按照之前给出的说明更好地解决 SSL 证书的问题。解决该问题后,您可以从 $clientConfigArr
RequestOptions::VERIFY => false
Guzzle 有用链接:
Request
Request Options
How to check if a request or response has a specific header.
Client
Response
Exceptions
您可能不应该执行 GET
请求,而应执行 POST
请求,正如 Martin 提到的那样。如果您想使用不同的方法发出请求,则:
对于 POST
方法,您很可能还需要发送一个 body,您可以创建一个 POST
Request
并附加 body这样:
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);
如果您要执行 POST
请求(或任何其他方法),如果您需要传递一些 body 内容(可能是您的令牌),请查看 api 文档
旁注:
在 Guzzle 代码示例中而不是 require I used require_once 因为:
if the code from a file has already been included, it will not be included again.
因此,您可以根据需要多次使用 may 文件:
require_once "../vendor/autoload.php";
一切都会正常工作,与没有 once
的 require
相反,后者通常会使您重复声明错误。
为了使 windows 像反斜杠一样,您可以使用 DIRECTORY_SEPARATOR and DIR 作为当前目录。像这样使用它们:
require_once __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
以防万一你想知道。