9

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

我从 PSR-7 请求开始:

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

我添加了身份验证标头,它正确地针对 API 进行身份验证:

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

然后我添加请求正文:

// 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]);

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

'args=dot&access_token=123456789'

这应该可以工作,并且可以在命令行 ( -d access_token=123456789) 中使用 curl,但是在发送 cia curl (6.x) 时,API 也无法看到该参数。

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

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

那么这里可能出了什么问题呢?是参数没有被发送,还是以错误的格式发送(可能'='被编码?),或者可能使用了错误的内容类型?使用 Guzzle 时很难看到“在线”发送的内容,因为 HTTP 消息经过格式化并发送了很多层。

编辑:调用本地测试脚本而不是远程 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

所以看起来身体被发送,所以我想还缺少其他东西来告诉远程 API 如何解释那个身体。

编辑:确实有效的命令行 curl 发送到相同的测试脚本,在请求中为我提供了两个额外的标头字段:

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

我猜这是 Guzzle 请求中缺少的内容类型标头,这是问题的根源。那么这是一个 Guzzle 错误吗?它不应该总是根据文档中列出的假设发送 Content-Type吗?

4

2 回答 2

15

标题Content-Type是问题。Content-Type通常,Guzzle 会握住你的手并插入它认为必要的标题,并根据你给它的内容以及给它的方式做出很好的猜测。

使用 Guzzle 的 PSR-7 消息,这种手持操作都没有完成。它严格地将所有标题留给您处理。因此,当向 PSR-7 添加 POST 参数时Request,您必须显式设置 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 查询字符串,然后将其粘贴到流中,但您已经有了。可能有一种更简单的方法来处理这个问题(例如,我不知道的包装类),在接受这个答案之前,我会等着看是否有任何出现。

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

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

哪里$boundary可以是类似的东西uniq(),用于构造多部分体。

于 2016-01-03T15:08:46.837 回答
15

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

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

可用文档Guzzle 请求选项

编辑:但是,如果您的请求正在 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, '&')
);
于 2016-01-08T21:24:59.970 回答