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

异步函数在Express正常运行,迁移至NestJS后报错问题咨询

问题根源分析

你遇到的这个错误,核心原因是auth对象的属性名和mssql-ssrs包的预期不匹配,导致httpntlm库在创建NTLM认证消息时拿到了undefined的用户名参数,进而触发了Buffer类型错误。

我们来具体拆解:

  • 看你提供的getReportByUrl源码,里面读取认证信息时用的是auth.userName(驼峰式的userName):
    username: auth.userName,
    
  • 但你在Express和NestJS的代码里,传入的auth对象属性都是username(全小写的name):
    const auth = { username: 'USERNAME', password: 'PASSWORD', domain: 'dmz' };
    

至于为什么Express里能正常运行?大概率是你Express项目中使用的mssql-ssrs版本较旧,当时的实现兼容了username这个属性名;而NestJS项目里的包版本更新后,严格按照userName来取值,导致username参数变成undefined,最终触发了httpntlm的Buffer类型错误。

解决方案

1. 修正auth对象的属性名

把username改成userName,和mssql-ssrs的预期一致:

const auth = { userName: 'USERNAME', password: 'PASSWORD', domain: 'dmz' };

2. 修复getReport方法的异步逻辑问题

你的NestJS Service里的fs.writeFile是异步回调式的,回调里的return不会影响外层的Promise返回值,建议改成使用fs.promises.writeFile来实现异步等待,确保返回值正确:

async getReport(): Promise<string> {
  const serverUrl = 'http://reportServerName/ReportServer/ReportExecution2005.asmx';
  ssrs.setServerUrl(serverUrl);
  const reportPath = '/ApplicationPortalReports/TestReportNew';
  const fileType = 'word';
  const parameters = {ApplicationId: 3, TrainingCardId: 267, PortalPersonId: 52 };
  const auth = { userName: 'USERNAME', password: 'PASSWORD', domain: 'dmz' }; // 修正属性名
  let report;
  try {
    report = await ssrs.reportExecution.getReportByUrl(reportPath, fileType, parameters, auth);
  } catch (error) {
    console.error('获取报表失败:', error);
    return '报表获取失败';
  }

  try {
    await fs.promises.writeFile('ReportApiTest.doc', report); // 使用promise版本
    console.log('Data written');
    return 'File Written Succesfully';
  } catch (error) {
    console.error('写入文件失败:', error);
    return 'File Write Error';
  }
}

3. 补充mssql-ssrs源码的潜在问题

顺带提一下,你提供的getReportByUrl实现里,try/catch块只处理了config创建的错误,但没有把错误传递出去(只是调用了report.errorHandler(err)),导致如果这里出错,Promise会一直处于pending状态。建议修改这个方法,在catch里reject错误:

async function getReportByUrl(reportPath, fileType, params, auth) {
  let config;
  try {
    config = {
      binary: true,
      username: auth.userName,
      password: auth.password,
      workstation: auth.workstation,
      domain: auth.domain,
      url: soap.getServerUrl() + "?" + (testReportPath(reportPath).replace(/\s/g, '+')) + "&rs:Command=Render&rs:Format=" + reportFormat(fileType) + formatParamsToUrl(params)
    };
  } catch (err) {
    report.errorHandler(err);
    return Promise.reject(err); // 新增:reject错误
  }
  return new Promise((resolve, reject) => {
    config.url = encodeURI(config.url);
    httpntlm.post(config, function (err, res) {
      if (res?.statusCode === 500) {
        reject(res);
      }
      if (err || res?.statusCode !== 200) {
        reject(err || new Error(`请求失败,状态码:${res?.statusCode}`));
      } else {
        resolve(res.body);
      }
    })
  })
}

这样就能确保错误能被上层的try/catch捕获,避免出现未处理的Promise rejection。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 07:38:12