【问题标题】:Guzzle not sending PSR-7 POST body correctlyGuzzle 未正确发送 PSR-7 POST 正文
【发布时间】:2020-05-23 17:49:20
【问题描述】:

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

我从 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 消息经过格式化并发送了很多层。

编辑:调用 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

所以看起来主体 正在 被发送,所以我猜缺少其他东西来告诉远程 API 如何解释该主体。

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

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

我猜这是 Guzzle 请求中缺少的内容类型标头,这是问题的根源。那么这是一个 Guzzle 错误吗?它不应该总是发送一个 Content-Type,基于它所做的假设是 listed in the documentation 吗?

【问题讨论】:

    标签: guzzle psr-7


    【解决方案1】:

    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, '&')
    );
    

    【讨论】:

    • 所以 Guzzle client 将放入正确的 Content-Type 标头,前提是您以正确的方式为其提供原始数据(form_params 是这里的触发器,我猜测)。在构造 PSR-7 请求消息时,Guzzle 将不会添加任何没有明确给出的标头。我的目标是构建一个 PSR-7 消息,该消息可以通过任何支持 PSR-7 消息的 HTTP 客户端发送,尽可能纯粹地看到它可以以尽可能可移植的方式完成,因此只使用 PSR辅助 Guzzle/PSR-7 包上的 -7 个方法。
    • 引号内Bearer 后面应该有一个空格。
    • 正确,客户端在使用 'auth' 和 'form_params' 请求选项时会自动插入正确的内容类型标头和身份验证标头。这是在手动构建与其他 PSR7 兼容包一起使用的请求时必须记住的事情。
    • 对于 post 请求 'Content-Type' => 'application/json' 和请求中的第 4 个参数为 json_encode($data)
    【解决方案2】:

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

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

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

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

    其中$boundary 可以类似于uniq(),用于构造多部分主体。

    【讨论】:

    • 这一行:$body = new \GuzzleHttp\Psr7\stream_for(http_build_query($params)); 实际上应该是$body = \GuzzleHttp\Psr7\stream_for(http_build_query($params)); 不需要new
    • stream_for() 确实是一个函数。它可以使用use function GuzzleHttp\Psr7\stream_for 导入,并且如果它与另一个同名的全局函数冲突,也可以使用别名。已更正答案,谢谢。
    猜你喜欢
    • 2016-04-16
    • 2020-11-08
    • 2018-07-18
    • 1970-01-01
    • 2015-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-26
    相关资源
    最近更新 更多