如何通过While循环调用API直至Locator为空并分文件存储结果
修正后的Azure函数API循环调用实现
原代码存在的问题
- 首次调用生成文件时未传入序号参数,会生成
testundefined.csv - 循环调用时未更新
locator参数,导致重复使用初始值,陷入死循环 - 终止条件判断错误,API返回空locator时通常是
null或空字符串,而非undefined - 数据数组
lsttis未在每次循环后重置,导致每次写入的文件包含之前所有批次的数据 - API域名前后不一致(首次调用是
someapi.test.com,循环内是someapi.com) - 缺少错误捕获机制,API调用或文件写入失败会直接导致函数崩溃
- 文件写入使用同步方法
fs.writeFileSync,在异步函数中不符合最佳实践
修正后的完整代码
const fs = require('fs').promises; const { parse } = require('json2csv'); const getJobResult = async (token) => { const baseUrl = "https://someapi.test.com/services/data/v59.0/jobs/query/750Pw000005PqqBIAS/results"; const requestOptions = { method: "GET", headers: { "Content-Type": 'application/json', "Accept": "text/csv", "Authorization": `Bearer ${token}` }, redirect: "follow" }; // 首次调用API let locator; let fileIndex = 0; try { // 第一次请求 const firstResp = await fetch(`${baseUrl}?maxRecords=500000`, requestOptions); if (!firstResp.ok) throw new Error(`首次请求失败: ${firstResp.status}`); const firstCsv = await firstResp.text(); locator = firstResp.headers.get('locator'); // 处理首次返回的数据并写入文件 const firstRows = firstCsv.split(/\r?\n|\r/).slice(1).filter(row => row.trim()); // 过滤空行 const firstData = firstRows.map(row => ({ AccessLevel: 'Read', ParentId: row.replace(/['"]+/g, ''), RowCause: 'Manual', UserOrGroupId: '11111111' })); if (firstData.length > 0) { await createCsvFile(firstData, fileIndex); fileIndex++; } // 循环调用直到locator为空 while (locator) { const loopResp = await fetch(`${baseUrl}?locator=${encodeURIComponent(locator)}&maxRecords=500000`, requestOptions); if (!loopResp.ok) throw new Error(`循环请求失败: ${loopResp.status}`); const loopCsv = await loopResp.text(); const newLocator = loopResp.headers.get('locator'); // 处理当前批次数据 const loopRows = loopCsv.split(/\r?\n|\r/).slice(1).filter(row => row.trim()); const loopData = loopRows.map(row => ({ AccessLevel: 'Read', ParentId: row.replace(/['"]+/g, ''), RowCause: 'Manual', UserOrGroupId: '11111111' })); if (loopData.length > 0) { await createCsvFile(loopData, fileIndex); fileIndex++; } // 更新locator,准备下一次循环 locator = newLocator; } } catch (error) { console.error('处理API请求或文件写入时出错:', error.message); throw error; // 抛出错误让Azure函数捕获处理 } }; const createCsvFile = async (data, index) => { const fields = ['AccessLevel', 'ParentId', 'RowCause', 'UserOrGroupId']; const csv = parse(data, { fields }); await fs.writeFile(`./test${index}.csv`, csv); }; // 导出函数供Azure函数调用 module.exports = getJobResult;
关键改进说明
- 统一API域名:使用
baseUrl变量确保所有请求域名一致 - 正确更新locator:每次循环后更新
locator值,避免死循环 - 重置数据数组:每次请求单独处理当前批次数据,避免数据累积
- 规范文件命名:首次调用使用
fileIndex=0,后续递增生成test0.csv、test1.csv等 - 错误处理:添加
try/catch捕获请求和文件操作错误,避免函数崩溃 - 异步文件写入:改用
fs.promises.writeFile符合异步函数的最佳实践 - 过滤空行:处理CSV数据时过滤空行,避免生成无效数据
- URL编码:使用
encodeURIComponent处理locator参数,防止特殊字符导致请求错误 - 变量命名规范:使用驼峰命名法提升代码可读性
内容的提问来源于stack exchange,提问作者Roger Holland
相关产品推荐
相关产品推荐

