26

次のように、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つのオプションが並んでいます:

  1. curl が予期しないことを指定し100-continueますか? (どのように?)
  2. curl が最後の応答のヘッダーのみを送り返すように指定しますか? (どのように?)
  3. ヘッダーを手動で確認し、100 Continueそれらとそれに続く空行を無視しますか? (その場合、手動で確認する必要がある、他に同様のことが起こる可能性はありますか?)

明らかな何かが欠けていない限り、人々はこれに出くわして何度も解決したと確信しています!

4

4 に答える 4

20

私は#1を選びます。以下を追加することで、curl に空の「Expect」ヘッダーを強制的に送信させることができます。

curl_setopt($ch, CURLOPT_HTTPHEADER,array("Expect:"));

あなたのコードに

手動で確認したい場合は、独自のヘッダー コールバックを定義し、コールバックを記述する必要があります ( curl_setopt docで CURLOPT_HEADERFUNCTION と CURLOPT_WRITEFUNCTION を探します)。これは単にすべての "HTTP/1.1 100 Continue" ヘッダーを無視するだけです。

于 2012-07-06T09:17:32.160 に答える
7

を使用して応答をヘッダーと本文に解析することにより、コメントで説明したアプローチを使用する別の方法を次に示します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.4
X-Powered-By: PHP/5.4.4
コンテンツの長さ: 112
コンテンツ タイプ: テキスト/プレーン

==エンドレスポンスヘッダー==

==レスポンスボディ==
**フォームデータ**
配列(2) {
  ["field1"]=>
  文字列(3)「フー」
  ["field2"]=>
  文字列(3)「バー」
}
**エンドフォームデータ**
==エンドボディ==
于 2012-07-06T14:23:48.573 に答える
0

私は 100s や 302s などでこれに出くわしましたが、面倒ですが、必要になる場合もあります (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 が何らかの方法で本文の内容をマークした場合はそれを好むでしょう) が、それは何年にもわたってうまく機能しています。乗り方を教えてください。

于 2012-07-06T09:24:04.027 に答える
0

私は同じ問題を抱えていましたが、この解決策は私にとってはうまくいきませんでした。

送信する前に、データ ポスト フィールドを準備する必要があります。

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
    ),
));}

2 つの配列を準備する必要があります。

このようにcurlを実行する前に、最後にこの関数を呼び出します。$r = curl_custom_postfields($curl, $post, $postfields_files);

于 2016-06-04T18:24:52.450 に答える