跨站点JSON数据传输后,PDO预处理插入数据库失败排查
问题排查与优化方案
一、现有代码的核心问题
源端代码问题
- SQL注入风险:直接将
$_GET['start']拼接进SQL语句,未做类型校验与预处理,若传入非数字值会导致SQL语法错误。 - 错误处理缺失:未开启PDO错误模式,查询失败时无报错信息,返回的JSON可能无效。
- JSON编码不严谨:未指定编码选项(如
JSON_UNESCAPED_UNICODE),中文会被转义;未处理编码失败场景。
接收端代码问题
- 语法错误:
print '</pre>'后多了一个闭合},导致脚本提前终止,根本无法执行插入逻辑。 - SQL语法错误:表名
table是MySQL关键字,未加反引号table,直接执行会报错。 - 预处理语句重复创建:每次循环都重新prepare相同SQL,浪费数据库资源。
- 错误处理缺失:未开启PDO错误模式,执行失败后无法获取具体报错信息。
- 数据校验缺失:未检查
json_decode是否成功,若源端返回无效JSON,后续逻辑直接崩溃。
二、快速排查步骤
- 开启PHP错误显示:在接收端开头添加代码,暴露语法或运行时错误:
error_reporting(E_ALL); ini_set('display_errors', 1); - 检查JSON有效性:解码后立即校验:
$a = json_decode($html, true); if (json_last_error() !== JSON_ERROR_NONE) { die('JSON解码失败: ' . json_last_error_msg()); } - 开启PDO错误模式:初始化PDO时添加错误处理配置:
$dbo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - 校验生成的SQL:查看打印的SQL语句,确认表名、字段名、占位符数量是否正确。
三、修复后的代码示例
源端优化代码
// 开启PDO错误模式 $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); if (isset($_GET['start']) && is_numeric($_GET['start'])) { $startId = (int)$_GET['start']; // 使用预处理查询避免注入 $stmt = $db->prepare("SELECT * FROM `table` WHERE `id` > ? LIMIT 5"); $stmt->execute([$startId]); $data = $stmt->fetchAll(PDO::FETCH_ASSOC); // 设置正确响应头,确保JSON编码可靠 header('Content-Type: application/json'); echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); } else { header('Content-Type: application/json'); echo json_encode([], JSON_THROW_ON_ERROR); }
接收端优化代码
error_reporting(E_ALL); ini_set('display_errors', 1); $a = json_decode($html, true); if (json_last_error() !== JSON_ERROR_NONE) { die('JSON解码失败: ' . json_last_error_msg()); } if (empty($a)) { echo '无新增数据'; exit; } // 开启PDO错误模式 $dbo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // 仅创建一次预处理语句,提升性能 $firstItem = reset($a); // 字段名加反引号,避免关键字冲突 $fields = implode(', ', array_map(fn($field) => "`$field`", array_keys($firstItem))); $placeholders = '(' . implode(', ', array_fill(0, count($firstItem), '?')) . ')'; $sql = "INSERT INTO `table` ($fields) VALUES $placeholders"; $stmt = $dbo->prepare($sql); try { // 开启事务,批量插入失败时回滚 $dbo->beginTransaction(); foreach ($a as $item) { $stmt->execute(array_values($item)); } $dbo->commit(); echo '数据插入成功'; } catch (PDOException $e) { $dbo->rollBack(); die('插入失败: ' . $e->getMessage()); }
四、更优跨站点数据传输方案
- 改用POST请求:避免GET的URL长度限制,同时可添加API密钥做身份验证,防止非法数据拉取。
- 增量数据标识优化:源端返回本次数据的最大ID,接收端下次以此为起始值,避免重复查询。
- 数据压缩传输:源端返回gzip压缩的JSON,减少带宽消耗;接收端解压后处理。
- 异步队列传输:若数据传输频繁,可采用Redis队列、RabbitMQ等异步方案,避免实时请求的延迟与失败。
- 数据校验机制:两端添加字段签名校验,确保传输过程中数据未被篡改。
内容的提问来源于stack exchange,提问作者Radium Chris
相关产品推荐
相关产品推荐

