17370845950

如何解决 cURL 发送大体积 JSON 数据时返回 404 的问题

当使用 curl 向 php 后端发送较大 json 字符串时,若直接以字符串形式传入 `curlopt_postfields`,apache 可能因请求体解析异常(如误判为非标准 post)而返回 404;正确做法是将 json 封装为关联数组键值对,并确保 content-type 与服务端接收逻辑一致。

在 Zend Server 2019.7(PHP 7.3 + Apache 2.4)环境中,你遇到的「小数据正常、大数据返回 404」现象,并非由 post_max_size 或 max_input_vars 等常见配置限制导致,而根本原因是 cURL 的 CURLOPT_POSTFIELDS 行为机制被 Apache/Zend Server 误解为“非标准表单提交”,从而触发路由或模块层面的 404 响应

? 问题根源解析

当你写:

curl_setopt($ch, CURLOPT_POSTFIELDS, $json_data); // $json_data 是纯 JSON 字符串

cURL 会以 Content-Type: application/x-www-form-urlencoded(默认)发送请求 —— 即使你手动设置了 'Content-Type: application/json' 头,PHP 的 $_POST 超全局变量仍无法自动解析该 JSON 字符串,且某些服务器环境(尤其是 Zend Server 的请求过滤模块)会对无 name=value 结构的原始 POST body 做异常处理,甚至直接拒绝路由到目标脚本(如 recon.php),最终返回 404。

✅ 注意:404 并非来自 PHP 应用层(如文件不存在),而是 Apache/Zend Server 在请求预处理阶段因无法识别有效 POST 格式而中断了请求分发。

✅ 正确解决方案:封装为表单键值对 + 显式声明 Content-Type

将 JSON 字符串作为某个字段的值,通过数组方式提交,强制 cURL 使用 multipart/form-data 或保持 x-www-form-urlencoded 但结构合规:

$json_data = '{ "Recon": [ /* ... large JSON array ... */ ] }';

$url = 'http://192.168.1.100/projectname/recon.php';

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_POST, 1);

// ✅ 关键修复:将 JSON 作为数组字段值(而非裸字符串)
curl_setopt($ch, CURLOPT_POSTFIELDS, ['json_payload' => $json_data]);

// ✅ 必须显式声明 Content-Type(否则 Apache 可能忽略)
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'Accept: application/json'
]);

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);

$result = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo "HTTP Code: $http_code\n";
echo $result;

? 服务端 recon.php 需同步适配

由于你已改用表单字段提交(json_payload),服务端不能再依赖 file_get_contents('php://input')(此时为空),而应从 $_POST 中读取并解码:

// recon.php
if (isset($_POST['json_payload'])) {
    $raw_json = $_POST['json_payload'];
    $data = json_decode($raw_json, true);

    if (json_last_error() !== JSON_ERROR_NONE) {
        http_response_code(400);
        echo json_encode(['error' => 'Invalid JSON']);
        exit;
    }

    // ✅ 正常处理 $data['Recon']...
    echo json_encode(['status' => 'success', 'count' => count($data['Recon'])]);
} else {
    http_response_code(400);
    echo json_encode(['error' => 'Missing json_payload']);
}

⚠️ 补充注意事项

  • 不要混淆 application/json 与表单提交:若坚持用 php://input 接收原始 JSON,则必须确保 CURLOPT_POSTFIELDS 传字符串 + Content-Type: application/json,且 Apache 的 mod_security、mod_proxy 或 Zend Server 的 Web Application Firewall(WAF)未拦截非常规 Content-Type 请求 —— 这在 Zend Server 中尤为常见。

  • 验证实际请求头:用 curl -v 或 Wireshark 抓包确认真实发出的 Content-Type 和 body 结构。

  • 检查 Zend Server 特定限制:进入 Zend Server Admin UI → Server ConfigPHP Settings,确认 suhosin.post.max_name_length、suhosin.request.max_value_length(如有启用 Suhosin)未截断长字段名或值。

  • 替代方案(推荐用于纯 API 场景):如需真正 RESTful 设计,建议统一使用 file_get_contents('php://input') + 原始 JSON 提交,并在 Apache 配置中显式允许:

    # 在虚拟主机或 .htaccess 中
    
        RewriteEngine On
        # 确保 JSON 请求不被重写规则误判
        RewriteCond %{CONTENT_TYPE} application/json
        RewriteRule .* - [E=HTTP_CONTENT_TYPE:application/json]
    

通过将 JSON 封装为 CURLOPT_POSTFIELDS 数组字段,既规避了 Apache 对裸 JSON body 的兼容性陷阱,又保持了向后兼容性与调试便利性 —— 这是 Zend Server 等企业级 PHP 环境中处理大体积 JSON POST 的稳健实践。