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

NestJS调用NetSuite REST API带q参数查询时出现INVALID_LOGIN错误

问题:NetSuite API带q参数请求返回INVALID_LOGIN错误

我正在使用NetSuite API,按照官方文档通过q参数查询员工数据,端点示例为GET https://demo123.suitetalk.api.netsuite.com/services/rest/record/v1/customer?q=email START_WITH barbara。Postman调用该端点可正常获取结果,但在NestJS代码中调用带q参数的请求时,收到错误'o:errorCode': 'INVALID_LOGIN'。无参数的请求可正常执行,推测问题与OAuth 1.0认证或签名生成方式有关,求解决建议。

我的代码

async conectNetSuiteTests(): Promise<any> {
  const baseUrl = `${NETSUITEURL}}/services/rest/record/v1/employee?q=custentity_bit_lt_entitydocument+CONTAIN+${documentEmployee}'`;

  const oauthNonce = crypto.randomBytes(5).toString('hex');
  const oauthTimestamp = Math.floor(Date.now() / 1000);
  const oauthSignatureMethod = 'HMAC-SHA256';
  const oauthVersion = '1.0';
  const realm = `5900181_SB1`;

  const oauthParameters = {
      oauth_consumer_key: process.env.CONSUMER_KEY,
      oauth_token: process.env.ACCESS_TOKEN,
      oauth_nonce: oauthNonce,
      oauth_timestamp: oauthTimestamp,
      oauth_signature_method: oauthSignatureMethod,
      oauth_version: oauthVersion,
  };

  // Ordenar los parámetros de OAuth alfabéticamente por clave
  const sortedParameters = Object.keys(oauthParameters)
      .sort()
      .map((key) => `${key}=${encodeURIComponent(oauthParameters[key])}`)
      .join('&');

  console.log('sor', sortedParameters)
  // Crear la cadena base para la firma
  const signatureBaseString = `GET&${encodeURIComponent(baseUrl)}&${encodeURIComponent(sortedParameters)}`;

  // Crear la clave de firma
  const signingKey = `${process.env.CONSUMER_SECRET}&${process.env.ACCESS_TOKEN_SECRET}`;

  // Calcular la firma HMAC-SHA256
  const hmac = crypto.createHmac('sha256', signingKey);
  hmac.update(signatureBaseString);
  const oauthSignature = hmac.digest('base64');

  // Construir la cabecera de autorización
  const authorizationHeader = `OAuth realm="${realm}", oauth_consumer_key="${process.env.CONSUMER_KEY}", oauth_token="${process.env.ACCESS_TOKEN}", oauth_nonce="${oauthNonce}", oauth_timestamp="${oauthTimestamp}", oauth_signature="${oauthSignature}", oauth_signature_method="${oauthSignatureMethod}", oauth_version="${oauthVersion}"`;

  // Configuración de la solicitud Axios
  console.log('authorizationHeader', authorizationHeader)
  const axiosConfig: AxiosRequestConfig = {
      method: 'get',
      url: baseUrl, 
      headers: {
          'Prefer': 'transient',
          'Content-Type': 'application/json',
          'Authorization': authorizationHeader,
      },
  };

  try {
      const response = await axios(axiosConfig);

      return {
          "PRO":response.data.custentity_ks_empleado_proveedor_employe.refName.split(' ')[0].split('-')[1],
          "CLI":response.data.custentitycustentity_ks_empleado_cliente.refName.split(' ')[0].split('-')[1],
      };
  } catch (error) {
      console.error('error', error);
      console.error('error', error['response']['data']['o:errorDetails']);
      throw error;
  }
}

解决建议

  • 修正URL语法错误:你的baseUrl存在两处语法错误:多了一个闭合大括号},末尾还有多余的单引号'。同时必须对documentEmployee进行URL编码,避免特殊字符破坏请求结构。修正后的URL拼接:

    const baseUrl = `${NETSUITEURL}/services/rest/record/v1/employee?q=custentity_bit_lt_entitydocument+CONTAIN+${encodeURIComponent(documentEmployee)}`;
    
  • 将查询参数纳入OAuth签名计算:OAuth 1.0要求所有请求参数(包括查询参数)都要加入签名基字符串的参数集合,你当前只使用了OAuth参数,漏掉了q参数。需要把查询参数合并到参数列表中再排序编码:

    // 合并OAuth参数和查询参数
    const requestParams = {
      ...oauthParameters,
      q: `custentity_bit_lt_entitydocument+CONTAIN+${documentEmployee}`
    };
    
    // 对合并后的参数排序编码
    const sortedParameters = Object.keys(requestParams)
      .sort()
      .map((key) => `${key}=${encodeURIComponent(requestParams[key])}`)
      .join('&');
    
  • 拆分签名基字符串的URL与参数:签名基字符串中的URL必须是不带查询参数的基础URL,不能包含q参数。正确的做法是分离基础URL和查询参数:

    const baseUrlWithoutQuery = `${NETSUITEURL}/services/rest/record/v1/employee`;
    const queryParams = { q: `custentity_bit_lt_entitydocument+CONTAIN+${documentEmployee}` };
    
    // 签名基字符串使用无参数的基础URL
    const signatureBaseString = `GET&${encodeURIComponent(baseUrlWithoutQuery)}&${encodeURIComponent(sortedParameters)}`;
    
    // Axios请求用params传递查询参数,避免手动拼接URL的错误
    const axiosConfig: AxiosRequestConfig = {
      method: 'get',
      url: baseUrlWithoutQuery,
      params: queryParams,
      headers: {
        'Prefer': 'transient',
        'Content-Type': 'application/json',
        'Authorization': authorizationHeader,
      },
    };
    
  • 校验OAuth头格式:对比Postman生成的Authorization头,确保你的头参数没有多余空格、引号为英文双引号,参数间用逗号加空格分隔,避免格式错误导致认证失败。

内容的提问来源于stack exchange,提问作者Jhon Alejandro Suarez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 22:42:05