异步函数在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
相关产品推荐
相关产品推荐

