NextJS调用Google Docs API批量更新报错:Must specify at least one request
问题排查与解决方案
问题原因
你在调用Google Docs API的batchUpdate接口时存在两个核心错误:
- Fetch API的请求配置中,请求体对应的属性是
body而非你使用的payload,这导致API完全没有接收到你定义的requests数组,因此返回"Must specify at least one request"的错误提示。 - 未在请求头中添加
Content-Type: application/json,API无法正确解析你发送的JSON格式数据。
修复后的完整代码
const createDoc = (teamName: string) => { const fileName: string = `${teamName} Scouting Report 2023`; const accessToken = session?.accessToken; // 第一步:创建空白文档 fetch(`https://docs.googleapis.com/v1/documents?title=${encodeURIComponent(fileName)}`, { method: 'POST', headers: new Headers({ Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' }), }) .then(res => { if (!res.ok) throw new Error(`创建文档失败: ${res.statusText}`); return res.json(); }) .then(val => { const documentId = val.documentId; console.log('文档创建成功,ID:', documentId); // 构造批量更新请求体(包含球员名称+数据表格) const batchRequestBody = { requests: [ // 插入球员名称 { insertText: { text: '"PLAYER 1"\n', endOfSegmentLocation: {} } }, // 插入3列2行的表格(表头+数据行) { insertTable: { rows: 2, columns: 3, endOfSegmentLocation: {} } }, // 填充表头(使用表格单元格定位,比固定索引更可靠) { insertText: { text: 'Points', location: { tableCellLocation: { tableStartLocation: { endOfSegmentLocation: {} }, rowIndex: 0, columnIndex: 0 } } } }, { insertText: { text: 'Rebounds', location: { tableCellLocation: { tableStartLocation: { endOfSegmentLocation: {} }, rowIndex: 0, columnIndex: 1 } } } }, { insertText: { text: 'Assists', location: { tableCellLocation: { tableStartLocation: { endOfSegmentLocation: {} }, rowIndex: 0, columnIndex: 2 } } } }, // 填充球员数据 { insertText: { text: '12.4', location: { tableCellLocation: { tableStartLocation: { endOfSegmentLocation: {} }, rowIndex: 1, columnIndex: 0 } } } }, { insertText: { text: '4.5', location: { tableCellLocation: { tableStartLocation: { endOfSegmentLocation: {} }, rowIndex: 1, columnIndex: 1 } } } }, { insertText: { text: '5', location: { tableCellLocation: { tableStartLocation: { endOfSegmentLocation: {} }, rowIndex: 1, columnIndex: 2 } } } } ] }; // 第二步:执行批量更新 return fetch( `https://docs.googleapis.com/v1/documents/${documentId}:batchUpdate`, { method: 'POST', headers: new Headers({ Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' }), body: JSON.stringify(batchRequestBody) // 替换为正确的body属性 } ); }) .then(res => { if (!res.ok) throw new Error(`批量更新失败: ${res.statusText}`); return res.json(); }) .then(updateRes => { console.log('文档更新成功:', updateRes); }) .catch(err => console.error('请求错误:', err)); };
额外优化建议
- 改用Async/Await:替换链式
then写法,提升代码可读性:const createDoc = async (teamName: string) => { try { const fileName = `${teamName} Scouting Report 2023`; const accessToken = session?.accessToken; if (!accessToken) throw new Error('未获取到访问令牌'); // 创建文档 const createRes = await fetch(`https://docs.googleapis.com/v1/documents?title=${encodeURIComponent(fileName)}`, { method: 'POST', headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' } }); if (!createRes.ok) throw new Error(`创建文档失败: ${createRes.statusText}`); const docData = await createRes.json(); const documentId = docData.documentId; // 批量更新文档 const batchRes = await fetch(`https://docs.googleapis.com/v1/documents/${documentId}:batchUpdate`, { method: 'POST', headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ /* 请求体内容 */ }) }); if (!batchRes.ok) throw new Error(`批量更新失败: ${batchRes.statusText}`); const updateData = await batchRes.json(); console.log('更新成功:', updateData); } catch (err) { console.error('错误:', err); } }; - 类型安全:为请求体定义TypeScript接口,避免
any类型带来的隐患:interface InsertTextRequest { insertText: { text: string; location?: { tableCellLocation: any }; endOfSegmentLocation?: {}; }; } interface InsertTableRequest { insertTable: { rows: number; columns: number; endOfSegmentLocation?: {}; }; } type DocsBatchRequest = InsertTextRequest | InsertTableRequest; - 批量处理多球员:可以通过循环生成多个
requests对象,一次性插入所有球员的名称和数据表格。
内容的提问来源于stack exchange,提问作者Matthew Dwyer
相关产品推荐
相关产品推荐

