Guzzle 没有正确发送 PSR-7 POST body

Guzzle not sending PSR-7 POST body correctly

它要么没有被发送,要么没有被正确接收。直接从命令行(使用 -d 选项)或从 PHP(使用 CURLOPT_POSTFIELDS)使用 curl 确实有效。

我从一个 PSR-7 请求开始:

$request = GuzzleHttp\Psr7\Request('POST', $url);

我添加身份验证 header,它根据 API 正确进行身份验证:

$request = $request->withHeader('Authorization', 'Bearer ' . $accessToken);

然后我添加请求body:

// The parameter for the API function
$body = \GuzzleHttp\Psr7\stream_for('args=dot');
$request = $request->withBody($body);

我可以发消息给API:

$client = new \GuzzleHttp\Client();
$response = $client->send($request, ['timeout' => 2]);

我得到的响应表明 "args" 参数根本没有被 API 看到。我尝试将身份验证令牌移动到 args:

'args=dot&access_token=123456789'

这应该可以工作,确实可以从命令行使用 curl (-d access_token=123456789),但是 API 在发送时也看不到该参数cia curl (6.x) 如上。

我可以看到消息 确实 包含 body:

var_dump((string)$request->getBody());
// string(8) "args=dot"
// The "=" is NOT URL-encoded in any way.

那么这里可能出了什么问题?参数是否未发送,或者它们是否以错误的格式发送(可能正在编码“=”?),或者可能使用了错误的 content-type?使用 Guzzle 时很难看到正在发送的内容 "on the wire",因为 HTTP 消息被格式化并发送了很多层。

编辑:调用 local test script 而不是远程 API,我得到了这条原始消息的详细信息:

POST
CONNECTION: close
CONTENT-LENGTH: 62
HOST: acadweb.co.uk
USER-AGENT: GuzzleHttp/6.1.1 curl/7.19.7 PHP/5.5.9

args=dot&access_token=5e09d638965288937dfa0ca36366c9f8a44d4f3e

所以看起来 body 正在发送 ,所以我想还缺少其他东西来告诉远程 API 如何解释 body.

编辑:确实有效的 command-line curl,发送到同一个测试脚本,在请求中给了我两个额外的 header 字段:

CONTENT-TYPE: application/x-www-form-urlencoded
ACCEPT: */*

我猜是 Guzzle 请求中缺少的 content-type header 才是问题的根源。那么这是一个 Guzzle 错误吗?它不应该总是根据它所做的假设 listed in the documentation 发送 Content-Type 吗?

Content-Type header 是问题所在。通常情况下,Guzzle会牵着你的手,插入它认为必要的header,并根据你给它的东西很好地猜测Content-Type,以及如何你给了。

通过 Guzzle 的 PSR-7 消息,hand-holding 中的 none 已完成。它严格地将所有 header 留给您处理。因此,当向 PSR-7 Request 添加 POST 参数时,您必须显式设置 Content-Type:

$params = ['Foo' => 'Bar'];
$body = \GuzzleHttp\Psr7\stream_for(http_build_query($params));
$request = $request->withBody($body);
$request = $request->withHeader('Content-Type', 'application/x-www-form-urlencoded');

将参数作为数组传递并让 Guzzle 计算其余部分的能力不适用于 Guzzle 的 PSR-7 实现。这有点笨拙,因为您需要将 POST 参数序列化为 HTTP 查询字符串,然后将其粘贴到流中,但您已经完成了。可能有更简单的方法来处理这个问题(例如我不知道的包装器class),我会等着看是否有任何问题出现,然后再接受这个答案。

另请注意,如果构建 multipart/form-data 请求消息,则需要将边界字符串添加到 Content-Type:

$request = $request->withHeader('Content-Type', 'multipart/form-data; boundary=' . $boundary);

其中 $boundary 可以类似于 uniq() 并且用于构造多部分 body。

GuzzleHttp\Client 提供所有必要的包装。

$response = $client->post(
    $uri,
    [
        'auth' => [null, 'Bearer ' . $token],
        'form_params' => $parameters,
]);

文档可用Guzzle Request Options

编辑:但是,如果您的请求在 GuzzleHttp\Pool 中使用,那么您可以将所有内容简单地放入以下内容:

$request = new GuzzleHttp\Psr7\Request(
    'POST',
    $uri,
    [
       'Authorization' => 'Bearer ' . $token,
       'Content-Type' => 'application/x-www-form-urlencoded'

    ],
    http_build_query($form_params, null, '&')
);