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

Firebase Functions(Node.js)发起HTTPS POST提交Office表单报400错误

问题描述

我正在创建Firebase Cloud Function实现自动提交Microsoft Office在线表单的功能,编写的每小时定时触发函数代码如下:

exports.covidForm = functions.pubsub.schedule('every 1 hours').onRun((context) => {

    var data = `{
  "startDate": "2022-07-04T05:27:42.157Z",
  "submitDate": "2022-07-04T05:31:30.748Z",
  "answers": "[{\"questionId\":\"rc4588b6d25e8418b9dc6ead534c83ae9\",\"answer1\":\"Name\"},{\"questionId\":\"r3e87261178f94361b4fa8b61bb6d62e4\",\"answer1\":\"ID\"},{\"questionId\":\"r357b74d9ed964c3da24f0586f8cb7332\",\"answer1\":\"email\"},{\"questionId\":\"r14db4cece08b49a09f249e7e87d06c51\",\"answer1\":\"site\"},{\"questionId\":\"rc85571bd0c0b4027833c7d2f18f38b53\",\"answer1\":\"No\"},{\"questionId\":\"ra9a3c908d3204e21b37c08bec50f983d\",\"answer1\":\"No\"},{\"questionId\":\"rcf571f318ea54bffa41364a4f967d88c\",\"answer1\":\"No\"},{\"questionId\":\"ra09058c8546e489e865cd6ffd862c23c\",\"answer1\":\"No\"}]"
}`;

    fetch("https://forms.office.com/formapi/api/.../forms('...')/responses", {
        method: 'POST',
        body: data,
        headers: {
            'Content-Type': 'application/json'
        }
    })
        .then(res => res.json())
        .then(json => functions.logger.log(json))
        .catch (err => functions.logger.log(err))
});

运行函数时收到如下400错误:

{"errors":{"":["The input was not valid."]},"type":"https://tools.ietf.org/html/rfc7231#section-6.5.1","title":"One or more validation errors occurred.","status":400,"traceId":"00-e52550913c0d8961deb46df79763ae95-06127511b66b74a6-00"} 

我在reqbin.com平台使用完全相同的JSON数据发起HTTPS POST请求可以成功提交表单,无法定位Firebase Functions环境下请求执行失败的原因,reqbin成功发起请求的截图如下:
reqbin POST请求成功截图

故障原因

代码存在4个核心问题直接导致请求失败:

  • 没有返回fetch调用的Promise链:Firebase Cloud Function执行时如果检测到回调里没有待处理的异步任务,会直接回收函数实例终止运行,当前写法下fetch发起后没有返回,函数很可能在请求还没完整发送到Office服务器的时候就被关闭,服务端收到残缺的请求体自然返回「输入无效」的400错误。
  • 手动拼接JSON字符串容错率极低:用模板字符串硬写多层嵌套的JSON结构,转义符很容易出现多写漏写的问题,哪怕一个转义符错误都会让请求体不符合接口格式要求。
  • 运行时兼容问题:Node.js 16及更早版本的Firebase Functions运行时没有内置全局fetch方法,如果没手动引入fetch依赖,调用行为本身就不符合预期。
  • 缺少必要请求头:Office Forms接口会校验请求来源特征,默认的服务端请求头很容易被拦截判定为非法请求。
修复方案

按以下步骤修改代码即可正常运行:

  1. 如果使用Node.js 16及更低版本的运行时,先在函数项目目录执行npm install node-fetch@2安装fetch依赖(v2版本支持CommonJS规范,适配Firebase Functions默认运行环境,v3为ESM模块不推荐直接使用),在代码顶部引入fetch。
  2. 不要手动拼接JSON字符串,直接构造原生JavaScript对象,发送请求时用JSON.stringify()自动完成序列化,彻底避免转义错误。
  3. 把fetch的整个Promise链return给onRun回调,保证函数会等待请求执行完成后再退出。
  4. 补充常用的浏览器请求头,绕过接口的基础反爬校验。
  5. 不要硬编码固定的startDate和submitDate,每次提交时动态生成当前时间,避免被接口判定为重复无效提交。

修复后的完整代码如下:

// Node16及以下版本加这行引入fetch,Node18+内置全局fetch可省略
const fetch = require('node-fetch');

exports.covidForm = functions.pubsub.schedule('every 1 hours').onRun((context) => {
    // 直接构造JS对象,不要手动拼JSON字符串
    const requestData = {
        startDate: new Date().toISOString(),
        submitDate: new Date().toISOString(),
        answers: JSON.stringify([
            {"questionId":"rc4588b6d25e8418b9dc6ead534c83ae9","answer1":"Name"},
            {"questionId":"r3e87261178f94361b4fa8b61bb6d62e4","answer1":"ID"},
            {"questionId":"r357b74d9ed964c3da24f0586f8cb7332","answer1":"email"},
            {"questionId":"r14db4cece08b49a09f249e7e87d06c51","answer1":"site"},
            {"questionId":"rc85571bd0c0b4027833c7d2f18f38b53","answer1":"No"},
            {"questionId":"ra9a3c908d3204e21b37c08bec50f983d","answer1":"No"},
            {"questionId":"rcf571f318ea54bffa41364a4f967d88c","answer1":"No"},
            {"questionId":"ra09058c8546e489e865cd6ffd862c23c","answer1":"No"}
        ])
    };

    // 一定要return整个Promise链
    return fetch("https://forms.office.com/formapi/api/.../forms('...')/responses", {
        method: 'POST',
        body: JSON.stringify(requestData),
        headers: {
            'Content-Type': 'application/json',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            'Origin': 'https://forms.office.com',
            'Referer': 'https://forms.office.com/'
        }
    })
    .then(res => {
        if (!res.ok) throw new Error(`请求失败,状态码:${res.status}`);
        return res.json();
    })
    .then(json => functions.logger.log("表单提交成功:", json))
    .catch (err => functions.logger.error("表单提交出错:", err))
});

内容的提问来源于stack exchange,提问作者Joshua Jack

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 06:36:21