如何从JSON API请求向SQL Server表批量插入多条记录(API接收JSON数组异常问题解决)
嘿,我一眼就看出问题出在哪了——你的API当前只能处理单个JSON对象的插入请求,但你传入的是一个JSON数组。当你把数组塞进req.body后,let proof = { ...req.body }会把整个数组当成一个单独的对象,而这个数组对象根本没有proofID、client这些属性,所以传给存储过程的参数全是undefined,最终数据库里就只能插入null值,也就只有自增ID是正常生成的。
接下来给你两种解决方案,你可以根据自己的需求选择:
方案1:快速适配数组(循环调用现有存储过程)
这种方式不用修改SQL代码,只需要调整Node.js逻辑,适合快速上线或者数据量不大的场景。
1. 修正API端点的处理逻辑
原来的端点只处理单个对象,现在要判断请求体是数组还是单个对象,同时把回调改成async/await,避免循环里的异步操作乱序:
app.post('/api/proofs', limiter, async (req,res) => { try { const results = []; // 判断请求体是否为数组 if (Array.isArray(req.body)) { // 遍历数组中的每一条数据 for (const proofItem of req.body) { const insertResult = await dboperations.addProof(proofItem); results.push(...insertResult.recordsets[0]); // 把插入的记录直接存入结果数组 } } else { // 处理单个对象的情况 const insertResult = await dboperations.addProof(req.body); results.push(...insertResult.recordsets[0]); } res.status(201).json(results); } catch (error) { console.error('插入失败:', error); res.status(500).json({ message: '插入数据时发生错误' }); } })
2. 给addProof函数增加输入防护
避免传入无效对象导致报错,同时处理属性为undefined的情况:
async function addProof(proof){ // 校验输入是否合法 if (!proof || typeof proof !== 'object') { throw new Error('传入的proof数据格式无效'); } try{ const pool = await sql.connect(configJobData); const insertProof = await pool.request() // 用空值合并运算符,把undefined转成null传给SQL .input('proofID', sql.Int, proof.proofID ?? null) .input('runSeq', sql.Int, proof.runSeq ?? null) .input('client', sql.VarChar, proof.client ?? null) .input('result', sql.VarChar, proof.result ?? null) .execute('insertProof'); return insertProof; } catch (error){ console.error('存储过程调用失败:', error); throw error; // 把错误抛给上层处理 } }
方案2:高效批量插入(用SQL表值参数,推荐)
如果你的数据量较大,循环调用存储过程会有性能瓶颈,这时候用SQL Server的表值参数一次性插入所有数据是更优的选择。
1. 在SQL Server创建表值类型
执行以下脚本,创建一个和Proofs表结构匹配的自定义表类型:
USE [JobData2] GO CREATE TYPE [dbo].[ProofTableType] AS TABLE( [proofID] int NULL, [runSeq] int NULL, [client] varchar(75) NULL, [result] varchar(15) NULL ) GO
2. 修改存储过程,支持批量插入
可以替换原有的insertProof,或者新建一个批量插入的存储过程:
ALTER PROCEDURE [dbo].[insertProofBatch] @Proofs [dbo].[ProofTableType] READONLY AS BEGIN SET NOCOUNT ON; -- 从表值参数中读取数据插入目标表 INSERT INTO JobData2.dbo.Proofs (proofID,runSeq,client,result) OUTPUT Inserted.[ID], Inserted.[proofID], Inserted.[client], Inserted.[runSeq], Inserted.[result] SELECT proofID, runSeq, client, result FROM @Proofs END GO
3. 在Node.js中添加批量插入函数
修改dboperations.js,新增处理批量插入的方法:
async function addProofBatch(proofsArray){ // 校验输入是否为非空数组 if (!Array.isArray(proofsArray) || proofsArray.length === 0) { throw new Error('需要传入非空的proof数组'); } try{ const pool = await sql.connect(configJobData); // 创建与表值类型匹配的SQL表对象 const table = new sql.Table(); table.columns.add('proofID', sql.Int, { nullable: true }); table.columns.add('runSeq', sql.Int, { nullable: true }); table.columns.add('client', sql.VarChar(75), { nullable: true }); table.columns.add('result', sql.VarChar(15), { nullable: true }); // 将数组中的每条数据添加到SQL表中 proofsArray.forEach(proof => { table.rows.add(proof.proofID, proof.runSeq, proof.client, proof.result); }); // 调用批量插入存储过程 const insertResult = await pool.request() .input('Proofs', table) .execute('insertProofBatch'); return insertResult.recordsets[0]; } catch (error){ console.error('批量插入失败:', error); throw error; } } // 导出新函数 module.exports = { addProof: addProof, addProofBatch: addProofBatch }
4. 更新API端点逻辑
现在可以同时支持单个对象和数组的批量插入:
app.post('/api/proofs', limiter, async (req,res) => { try { let results; if (Array.isArray(req.body)) { // 数组请求使用批量插入 results = await dboperations.addProofBatch(req.body); } else { // 单个对象使用原插入方法 const singleResult = await dboperations.addProof(req.body); results = singleResult.recordsets[0]; } res.status(201).json(results); } catch (error) { console.error('插入失败:', error); res.status(500).json({ message: '插入数据时发生错误' }); } })
现在用Postman测试你原来的JSON数组请求,应该就能得到两条完整的插入记录了,每个字段都有你传入的值,再也不会出现全null的情况啦!
内容的提问来源于stack exchange,提问作者Kraig Piercy
相关产品推荐
相关产品推荐

