You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 21:05:22