0

我遇到了一个问题,一旦 curl 被明确设置为使用 CURLOPT_HEADER,我从 REST 端点(SugarCRM 7)获得的所有响应都将包括完整的 HTTP 标头。我这样做是为了获取和存储用于会话管理的 Cookie,但直接的缺点是我从 curl_exec() 收到的响应是它包含所有标头,有效地破坏了将返回结果解释为有效 JSON 的任何尝试。POST 调用的实际响应发布在下面。

"HTTP/1.1 200 OK
Date: Thu, 25 Feb 2016 03:23:35 GMT
Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.1e-fips mod_fcgid/2.3.9 PHP/5.4.16
X-Powered-By: PHP/5.4.16
Set-Cookie: PHPSESSID=f0bsbjelfa9c0ada7qj1816986; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 75
Content-Type: application/json; charset=UTF-8

{"examplekey":"examplevalue"}"

如果 CURLOPT_HEADER 被禁用,响应会发送一个有效的 JSON 对象,但是没有有效的方法来跟踪会话的生命周期。通过跨 CRLF 拆分提取 JSON 数据应该是相当可预测的,但是必须有一种方法让 cURL 同时发送正文响应和标头(cookie 和所有),而不必冒险使用正则表达式 CRLF整个反应。不幸的是,在没有禁用 CURLOPT_HEADER 的情况下,我无法访问 cookie。

总而言之——保留来自 cURL 请求的标头,同时保留我可以处理的有效 JSON 序列化字符串的最佳方法是什么?

以下是用于此过程的方法。

public function RunCurl() {
  ob_start();

  //Set x-www-form-urlencoded header
  $Headers[] = 'Content-Type: application/x-www-form-urlencoded';
  $Headers[] = 'cache-control: no-cache';

  curl_setopt($this->CurlRequest, CURLOPT_VERBOSE, true);
  curl_setopt($this->CurlRequest, CURLOPT_URL, $this->SourceURL);
  curl_setopt($this->CurlRequest, CURLOPT_HEADER, 1);

  curl_setopt($this->CurlRequest, CURLOPT_RETURNTRANSFER, TRUE);
  curl_setopt($this->CurlRequest, CURLOPT_POSTFIELDS, http_build_query($this->EncapsulatedPostData));

  curl_setopt($this->CurlRequest, CURLOPT_HEADER, 1);
  curl_setopt($this->CurlRequest, CURLOPT_HTTPHEADER, $this->Cookie);

  try {
      $Results = curl_exec($this->CurlRequest);
      preg_match_all('/^Set-Cookie:\s*([^\r\n]*)/mi', $Results, $ms);
      $this->Cookie = $ms;
      curl_close($this->CurlRequest);
  } catch (Exception $ex) {
      echo 'Caught exception: ',  $ex->getMessage(), "\n";
      return false;
  }

  ob_end_flush();

  return json_decode(utf8_decode($Results));
}
4

3 回答 3

2

在 Body之后添加这两行将curl_exec包含您的 json 响应。

$Results = curl_exec($this->CurlRequest);

$header_size = curl_getinfo($this->CurlRequest, CURLINFO_HEADER_SIZE);
$body = substr($Results, $header_size);
于 2016-02-25T05:38:53.943 回答
1

另一种方法是使用该CURLOPT_HEADERFUNCTION选项 - 这允许您将标头发送到回调函数。一个应该接近您所追求的快速示例如下:

public function RunCurl() {
  ob_start();

  //Set x-www-form-urlencoded header
  $Headers[] = 'Content-Type: application/x-www-form-urlencoded';
  $Headers[] = 'cache-control: no-cache';

  curl_setopt($this->CurlRequest, CURLOPT_VERBOSE, true);
  curl_setopt($this->CurlRequest, CURLOPT_URL, $this->SourceURL);

  curl_setopt($this->CurlRequest, CURLOPT_RETURNTRANSFER, TRUE);
  curl_setopt($this->CurlRequest, CURLOPT_POSTFIELDS, http_build_query($this->EncapsulatedPostData));

  curl_setopt($this->CurlRequest, CURLOPT_HTTPHEADER, $this->Cookie);
  curl_setopt($this->CurlRequest, CURLOPT_HEADERFUNCTION, array($this, 'readHeader'));

  try {
      $Results = curl_exec($this->CurlRequest);
      curl_close($this->CurlRequest);
  } catch (Exception $ex) {
      echo 'Caught exception: ',  $ex->getMessage(), "\n";
      return false;
  }

  ob_end_flush();

  return json_decode(utf8_decode($Results));
}

private function readHeader($ch, $header) {
    $found = preg_match_all('/^Set-Cookie:\s*([^\r\n]*)/mi', $header, $ms);
    if ($found !== 0) {
        $this->Cookie = $ms;
    }
    return strlen($header);
}
于 2016-02-25T05:10:09.223 回答
0

根据HTTP 规范 (RFC7230),CRLF ("\r\n\r\n") 用作标题和正文部分的分隔符。因此,您可以通过以下方式获取响应的正文部分:

$body = substr($response, strpos($response, "\r\n\r\n")+4);

或者

list($header, $body) = explode("\r\n\r\n", $response, 2);
于 2016-02-25T04:50:46.330 回答