PHP Curl请求API多次后返回400错误但数据已入库求助
问题分析与修复方案
核心现象拆解
你遇到的问题是相同XML的POST请求偶尔返回400,但数据已成功写入API数据库,结合日志可定位关键差异:
- 异常响应的
content_type从application/soap+xml变为text/html,说明服务器返回了错误页面而非预期的SOAP响应 - 异常请求的
total_time从1.8s飙升至10s,大概率是服务器端处理超时或负载过高 - 上传数据量一致,说明数据已完整提交,400错误出现在响应环节而非请求提交阶段
针对性修复方案
1. 修复HTTP/2兼容性问题
你的代码强制指定了HTTP/2协议(CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0),但部分政府类API对HTTP/2的并发会话处理存在bug,会导致请求提交成功但响应异常。
修改代码:
将HTTP版本改为自动协商,让curl自行选择最优协议:
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_NONE, // 替换原有的CURL_HTTP_VERSION_2_0
2. 移除手动设置的Content-Length
curl会自动计算并设置Content-Length,你手动用strlen(trim($xml))计算的数值,可能因XML含多字节字符、trim改变内容等原因与实际上传长度不一致,服务器接收数据后完成入库,但校验header时返回400。
修改代码:
从请求头数组中删除Content-Length字段:
$headers = array( 'Accept' => 'application/xml', 'Content-Type' => 'application/xml', 'User-Agent' => 'spider', 'Cache-Control' => 'no-cache, must-revalidate' );
3. 优化Cookie与连接复用逻辑
你同时设置了CURLOPT_FRESH_CONNECT、CURLOPT_FORBID_REUSE、CURLOPT_COOKIESESSION,强制每次请求创建新连接和新会话,频繁请求时易触发服务器端会话异常。
修改代码:
- 若API无强制新会话要求,直接移除这三个选项,让curl复用连接以降低服务器压力
- 若必须使用新会话,每次请求后删除临时cookie文件避免残留:
curl_close($ch); unlink($cookiestore); // 请求结束后删除临时cookie文件
4. 捕获400响应的具体错误内容
异常时返回的text/html是服务器的错误页面,其中大概率包含具体错误信息,可帮你定位根源。
新增代码:
在获取响应后分离响应头与响应体,记录错误内容:
$xmlRes = curl_exec($ch); $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $response_body = substr($xmlRes, $header_size); // 当状态码为400时,记录错误内容 if ($res->status == 400) { error_log("[API 400错误] 响应内容:" . $response_body); }
5. 增加幂等重试机制
从total_time的变化来看,偶尔的400可能是服务器临时负载过高导致的响应超时,在确认接口幂等(重复提交不会重复入库)的前提下,增加重试逻辑:
$max_retries = 3; $retry_count = 0; $res = null; do { // 你的完整curl请求代码... $retry_count++; // 重试前短暂休眠,避免频繁请求 if ($res->status == 400 && $retry_count < $max_retries) { usleep(100000); // 休眠100ms } } while ($res->status == 400 && $retry_count < $max_retries);
内容的提问来源于stack exchange,提问作者Rolando Velazquez
相关产品推荐
相关产品推荐

