我使用 cURL 从 PHP 调用服务,如下所示:
$response = curl_exec($ch);
请求/响应标头如下所示:
请求:
POST /item/save HTTP/1.1
Host: services.mydomain.com
Accept: */*
Content-Length: 429
Expect: 100-continue
Content-Type: multipart/form-data
响应:
HTTP/1.1 100 Continue
HTTP/1.1 200 OK
Date: Fri, 06 Jul 2012 08:37:01 GMT
Server: Apache
Vary: Accept-Encoding,User-Agent
Content-Length: 256
Content-Type: application/json; charset=utf-8
后跟正文(JSON 编码的数据)。
问题是常见的是通过遇到的第一个空行在响应中拆分标头和正文,除了在这种情况下,空行在100 Continue
之后,因此其他所有内容都被推入正文 - 这不再是有效的 json :-)
所以我的问题是:处理这个问题的常用方法是什么?我有 3 个选项:
- 指定卷曲不应预期
100-continue
?(怎么? - 指定 curl 应该只发回最后一个响应的标头?(怎么?
- 手动检查
100 Continue
标题并忽略它们及其以下空行?(在这种情况下,是否还有其他类似的事情可能发生,我应该手动检查?
除非我错过了一些明显的东西,否则我相信人们已经偶然发现了这个问题并解决了很多次!
我会选择#1。您可以通过添加以下内容来强制 curl 发送空的"预期"标头:
curl_setopt($ch, CURLOPT_HTTPHEADER,array("Expect:"));
到您的代码
如果你想手动检查它,你应该定义你自己的头回调,也许写回调(在curl_setopt文档中查找CURLOPT_HEADERFUNCTION和CURLOPT_WRITEFUNCTION),它必须简单地忽略所有"HTTP/1.1 100 Continue"头。
这是另一种使用我在评论中描述的方法,通过使用CURLINFO_HEADER_SIZE
将响应解析为标头与正文:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://test/curl_test.php");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLINFO_HEADER_OUT, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);
// sets multipart/form-data content-type
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
'field1' => 'foo',
'field2' => 'bar'
));
$data = curl_exec($ch);
// if you want the headers sent by CURL
$sentHeaders = curl_getinfo($ch, CURLINFO_HEADER_OUT);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
curl_close($ch);
$header = substr($data, 0, $headerSize);
$body = substr($data, $headerSize);
echo "==Sent Headers=='n$sentHeaders'n==End Sent Headers=='n";
echo "==Response Headers=='n$headers'n==End Response Headers=='n";
echo "==Response Body=='n$body'n==End Body==";
我已经对此进行了测试,它会产生以下输出:
==发送的标头==POST/curl_test.php HTTP/1.1主机:测试接受:*/*内容长度:242预期:100-续内容类型:多部分/表单数据;边界=----------------------------D86AC263CE1B==结束发送标头====响应标头==HTTP/1.1 100 继续HTTP/1.1 200 OK日期:2012 年 7 月 6 日星期五 14:21:53 GMT服务器: Apache/2.4.2 (Win32) PHP/5.4.4X-Powered-By: PHP/5.4.4内容长度:112内容类型:文本/纯文本==结束响应标头====响应正文==**表单数据**数组(2) { ["字段 1"]=> 字符串(3) "呜" ["字段 2"]=> 字符串(3) "酒吧"}**结束表单数据**==端体==
我遇到了同样的问题,但这个解决方案确实对我有用,最后我找到了这种方法,而且很好:
我们必须在发送数据发布字段之前准备它们:
function curl_custom_postfields($curl, array $assoc = array(), array $files = array()) {
/**
* For safe multipart POST request for PHP5.3 ~ PHP 5.4.
* @param resource $ch cURL resource
* @param array $assoc "name => value"
* @param array $files "name => path"
* @return bool
*/
// invalid characters for "name" and "filename"
static $disallow = array("'0", "'"", "'r", "'n");
// build normal parameters
foreach ($assoc as $key => $value) {
$key = str_replace($disallow, "_", $key);
$body[] = implode("'r'n", array(
"Content-Disposition: form-data; name='"{$key}'"",
"",
filter_var($value),
));
}
// build file parameters
foreach ($files as $key => $value) {
switch (true) {
case false === $value = realpath(filter_var($value)):
case !is_file($value):
case !is_readable($value):
continue; // or return false, throw new InvalidArgumentException
}
$data = file_get_contents($value);
$value = call_user_func("end", explode(DIRECTORY_SEPARATOR, $value));
$key = str_replace($disallow, "_", $key);
$value = str_replace($disallow, "_", $value);
$body[] = implode("'r'n", array(
"Content-Disposition: form-data; name='"{$key}'"; filename='"{$value}'"",
"Content-Type: application/octet-stream",
"",
$data,
));
}
// generate safe boundary
do {
$boundary = "---------------------" . md5(mt_rand() . microtime());
} while (preg_grep("/{$boundary}/", $body));
// add boundary for each parameters
array_walk($body, function (&$part) use ($boundary) {
$part = "--{$boundary}'r'n{$part}";
});
// add final boundary
$body[] = "--{$boundary}--";
$body[] = "";
// set options
return @curl_setopt_array($curl, array(
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => implode("'r'n", $body),
CURLOPT_HTTPHEADER => array(
"Expect: 100-continue",
"Content-Type: multipart/form-data; boundary={$boundary}", // change Content-Type
),
));}
您必须准备两个数组:1-具有正常数据的帖子字段:(名称1 = val1,名称2 = val2,...)2-带有文件数据的发布字段:(name_file 1,path_file1,name_file2 = path_file2,..)
并在像这样执行 curl 之前最终调用此函数。$r = curl_custom_postfields($curl, $post, $postfields_files);
我在 100 秒和 302 秒等中遇到了这个问题,这很烦人,但有时需要(gdata 调用等),所以我会说让 curl 返回所有标题并提取正文略有不同。
我这样处理它(找不到我的实际代码,但你会明白的):
$response = curl_exec($ch);
$headers = array();
$body = array();
foreach(explode("'n'n", $response) as $frag){
if(preg_match('/^HTTP'/[0-9'.]+ [0-9]+/', $frag)){
$headers[] = $frag;
}else{
$body[] = $frag;
}
}
echo implode("'n'n", $headers);
echo implode("'n'n", $body);
我嫉妒冗长的黑客方法(如果 curl 以某种方式标记正文内容,我会更喜欢它),但它多年来效果很好。 让我们知道您的进展。