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

使用apollo-datasource-rest发起multipart-form POST请求遇400错误如何解决

错误原因排查
  • apollo-datasource-rest 默认会将POST请求的参数序列化为JSON格式,即使手动设置了multipart/form-data请求头,也没有实际将参数转换为对应的表单格式,且手动设置的请求头缺少multipart要求的boundary分隔符参数,服务端无法正常解析请求体,这是返回400错误的核心原因。
  • Salesforce OAuth2接口返回的访问令牌字段为蛇形格式access_token,代码中错误使用驼峰格式accessToken解构,即使请求成功也无法正常获取令牌。
  • 在构造函数中执行异步的令牌获取逻辑,会存在时序问题:后续业务接口发起请求时,令牌请求可能还未返回,导致请求头未携带有效令牌。
正确实现方案

首先安装form-data依赖,用于构造符合标准的multipart-form格式请求体:

npm install form-data

修改后的完整代码如下:

const { RESTDataSource } = require('apollo-datasource-rest');
const FormData = require('form-data');

process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

class SalesforceApi extends RESTDataSource {
  constructor() {
    super();
    this.initialize({});
    // 缓存令牌避免重复请求
    this.salesforceToken = null;
  }

  // 内置钩子,每次发起请求前自动执行,处理令牌逻辑
  async willSendRequest(request) {
    if (!this.salesforceToken) {
      this.salesforceToken = await this.getAccessToken();
    }
    request.headers.set('Authorization', `Bearer ${this.salesforceToken}`);
  }

  async getAccessToken() {
    console.log('Getting Salesforce access token');
    try {
      // 构造form-data请求体
      const form = new FormData();
      form.append('username', 'FILTERED@FILTERED');
      form.append('password', 'FILTERED');
      form.append('grant_type', 'password');
      form.append('client_id', 'FILTERED');
      form.append('client_secret', 'FILTERED');

      const response = await this.post(
        'https://test.salesforce.com/services/oauth2/token',
        form,
        {
          // 禁用默认的JSON序列化,直接返回form实例作为请求体
          serializer: (body) => body,
          // 自动生成带boundary的正确请求头
          headers: form.getHeaders()
        }
      );
      // 使用正确的字段名解构返回值
      const { access_token } = response;
      console.log(`Salesforce access token: ${access_token}`);
      return access_token;
    } catch (error) {
      console.log(`Get token error: ${error}`);
      throw error;
    }
  }
}

module.exports = SalesforceApi;

补充说明

如果不需要上传文件,Salesforce的OAuth2密码授权流也支持application/x-www-form-urlencoded格式,实现更简单,只需要将请求配置修改为:

const response = await this.post(
  'https://test.salesforce.com/services/oauth2/token',
  new URLSearchParams({
    username: 'FILTERED@FILTERED',
    password: 'FILTERED',
    grant_type: 'password',
    client_id: 'FILTERED',
    client_secret: 'FILTERED',
  }),
  {
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 17:06:05