首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP cURL 發送 JSON 資料完整實作 (含錯誤處理、Header 設定)

     編輯:PHP 相關知識

呼叫 RESTful API、微服務、第三方平台 (如微信、Line Bot) 時,常需以 POST 發送 Content-Type: application/json 資料。本文提供封裝良好的 postJson() 函式,涵蓋編碼、Header、超時、錯誤碼解析、重試機制。

核心函式

<?php
/**
 * 發送 JSON POST 請求
 * @param string $url 目標 URL
 * @param array|object $data 要發送的資料 (自動 json_encode)
 * @param array $options 額外選項
 * @return array ['success'=>bool, 'code'=>int, 'body'=>string, 'headers'=>array, 'error'=>string]
 */
function postJson($url, $data, $options = []) {
    $defaults = [
        'timeout'         => 10,          // 秒
        'connect_timeout' => 5,
        'headers'         => [],          // 額外 Header
        'retry'           => 0,           // 重試次數
        'retry_delay'     => 100000,      // 微秒
        'verify_ssl'      => true,        // 生產環境建議 true
    ];
    $opts = array_merge($defaults, $options);

    $payload = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    if (json_last_error() !== JSON_ERROR_NONE) {
        return ['success' => false, 'code' => 0, 'body' => '', 'headers' => [], 'error' => 'JSON 編碼失敗: ' . json_last_error_msg()];
    }

    $headers = array_merge([
        'Content-Type: application/json; charset=utf-8',
        'Content-Length: ' . strlen($payload),
        'Accept: application/json',
    ], $opts['headers']);

    $attempt = 0;
    do {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => $payload,
            CURLOPT_HTTPHEADER     => $headers,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER         => true,        // 回傳包含 Header
            CURLOPT_TIMEOUT        => $opts['timeout'],
            CURLOPT_CONNECTTIMEOUT => $opts['connect_timeout'],
            CURLOPT_SSL_VERIFYPEER => $opts['verify_ssl'],
            CURLOPT_SSL_VERIFYHOST => $opts['verify_ssl'] ? 2 : 0,
            CURLOPT_USERAGENT      => 'PHP-cURL-JSON/1.0',
        ]);

        $response = curl_exec($ch);
        $errno    = curl_errno($ch);
        $error    = curl_error($ch);
        $info     = curl_getinfo($ch);
        curl_close($ch);

        if ($errno === 0) {
            // 成功收到回應 (含 HTTP 4xx/5xx)
            $headerSize = $info['header_size'];
            $respHeaders = substr($response, 0, $headerSize);
            $body = substr($response, $headerSize);
            $httpCode = $info['http_code'];

            // 解析回應 Header 成陣列
            $hdrArr = [];
            foreach (explode("\r\n", $respHeaders) as $line) {
                if (strpos($line, ':') !== false) {
                    list($k, $v) = explode(':', $line, 2);
                    $hdrArr[trim($k)] = trim($v);
                }
            }

            return [
                'success' => ($httpCode >= 200 && $httpCode < 300),
                'code'    => $httpCode,
                'body'    => $body,
                'headers' => $hdrArr,
                'error'   => ($httpCode >= 200 && $httpCode < 300) ? '' : "HTTP $httpCode",
            ];
        }

        // 網路層錯誤:可重試
        $attempt++;
        if ($attempt <= $opts['retry']) {
            usleep($opts['retry_delay']);
        }
    } while ($attempt <= $opts['retry']);

    return ['success' => false, 'code' => 0, 'body' => '', 'headers' => [], 'error' => "cURL 錯誤 ($errno): $error"];
}

// 使用範例
$url = 'https://api.example.com/webhook';
$data = [
    'button' => [
        ['type'=>'click', 'name'=>'今日歌曲', 'key'=>'V1001_TODAY_MUSIC'],
        ['type'=>'click', 'name'=>'歌手簡介', 'key'=>'V1001_TODAY_SINGER'],
        ['name'=>'選單', 'sub_button'=>[
            ['type'=>'click', 'name'=>'Hello', 'key'=>'V1001_HELLO_WORLD'],
            ['type'=>'click', 'name'=>'贊', 'key'=>'V1001_GOOD'],
        ]],
    ],
];
$result = postJson($url, $data, ['timeout'=>15, 'retry'=>2]);
if ($result['success']) {
    echo "成功: " . $result['body'];
} else {
    error_log("發送失敗: " . $result['error']);
    echo "失敗: " . $result['error'];
}
?>

關鍵參數說明

選項預設用途
timeout10整個請求最長秒數 (含傳輸)
connect_timeout5TCP 連線建立最長秒數
verify_ssltrue驗證 SSL 憑證,生產必開;測試可關
retry0網路錯誤時重試次數 (不重試 HTTP 4xx/5xx)
headers[]額外 Header,如 Authorization: Bearer xxx

常見踩坑與解法

Q: 對方收到空資料 / $_POST 為空?
JSON 資料在 php://input,需用 json_decode(file_get_contents('php://input'), true) 讀取,而非 $_POST。
Q: 中文變亂碼?
確保 json_encode(..., JSON_UNESCAPED_UNICODE) 且 Header 含 charset=utf-8;資料庫/檔案儲存亦為 UTF-8。
Q: cURL error 60 (SSL certificate problem)?
下載 cacert.pem,設定 curl.cainfo=/path/to/cacert.pem (php.ini) 或 CURLOPT_CAINFO。
Q: 大檔案 JSON 記憶體不足?
改用 CURLOPT_READFUNCTION 流式讀取,或分批發送。

進階:非同步併發發送 (curl_multi)

function postJsonMulti(array $requests, $options = []) {
    // $requests = [['url'=>'...', 'data'=>[...]], ...]
    $mh = curl_multi_init();
    $handles = [];
    foreach ($requests as $i => $req) {
        $ch = curl_init($req['url']);
        $payload = json_encode($req['data'], JSON_UNESCAPED_UNICODE);
        curl_setopt_array($ch, [
            CURLOPT_POSTFIELDS => $payload,
            CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Content-Length: '.strlen($payload)],
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER => true,
        ] + ($options['curl_opts'] ?? []));
        curl_multi_add_handle($mh, $ch);
        $handles[$i] = $ch;
    }
    $running = null;
    do { curl_multi_exec($mh, $running); } while ($running);
    $results = [];
    foreach ($handles as $i => $ch) {
        $resp = curl_multi_getcontent($ch);
        $info = curl_getinfo($ch);
        // 解析同單一版本...
        $results[$i] = parseResponse($resp, $info);
        curl_multi_remove_handle($mh, $ch);
    }
    curl_multi_close($mh);
    return $results;
}
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved