Я вызываю службу из PHP с помощью cURL, например:
$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
? (Как?) 100 Continue
заголовков 100 Continue
и проигнорируйте их и их следующую пустую строку? (В этом случае, есть ли другие подобные вещи, которые могут произойти, что я должен проверить вручную?) Если я не пропущу что-то очевидное, я уверен, что люди наткнулись на это и решили его много раз!
Я выберу # 1. Вы можете заставить curl отправить пустой заголовок «Expect», добавив:
curl_setopt($ch, CURLOPT_HTTPHEADER,array("Expect:"));
к вашему коду
Если вы хотите проверить его вручную, вы должны определить свой собственный обратный вызов заголовка и, возможно, написать обратный вызов (посмотрите CURLOPT_HEADERFUNCTION и CURLOPT_WRITEFUNCTION в файле curl_setopt ), который просто игнорирует все заголовки «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-продолжить Content-Type: multipart / form-data; граница = ---------------------------- d86ac263ce1b == Конец отправленных заголовков == == Ответы заголовков == HTTP / 1.1 100 Продолжить HTTP / 1.1 200 OK Дата: Пт, 06 Июл 2012 14:21:53 GMT Сервер: Apache / 2.4.2 (Win32) PHP / 5.4.4 X-Powered-By: PHP / 5.4.4 Контент-длина: 112 Content-Type: text / plain == Конечные ответы == == Тело ответа == ** ФОРМА ДАННЫХ ** array (2) { [ "Поле1"] => строка (3) "foo" [ "Поле2"] => строка (3) "bar" } ** ДАННЫЕ КОНЕЧНОЙ ФОРМЫ ** == Конец тела ==
Я столкнулся с этим с 100 и 302 и т. Д. Это раздражает, но иногда необходимо (вызовы gdata и т. Д.), Поэтому я бы сказал, что оставить завиток, возвращая все заголовки и немного вытягивая тело.
Я обрабатываю это так (не могу найти мой фактический код, но вы получите идею):
$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);
Я жалуюсь на долгоживущий хакерский метод (предпочтет, чтобы он скручивался каким-то образом), но с годами он работал хорошо. сообщите нам, как вы поживаете.
у меня была та же проблема, но это решение действительно замечает работу для меня, окончательно я нашел этот метод и все его прекрасное:
мы должны подготовить поля данных перед отправкой:
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-поле сообщения с нормальными данными: (name1 = val1, name2 = val2, …) 2- почтовое поле с файловыми данными: (name_file 1, path_file1, name_file2 = path_file2, ..)
и окончательно вызовите эту функцию, прежде чем выполнять завиток, подобный этому. $ r = curl_custom_postfields ($ curl, $ post, $ postfields_files);