注册路由中smsService报TypeError: req.post is not a function错误排查求助
你遇到的这个错误其实很好定位——Express的req(请求对象)本身并没有post方法,你混淆了Express用来定义路由的app.post和用来发送外部HTTP请求的客户端工具。下面一步步帮你排查和修复:
错误原因分析
在你的smsService.js里,你尝试调用req.post()来发送短信API请求,但这里的req是Express从客户端接收到的请求对象,它只包含当前请求的信息(比如req.body、req.file等),并没有提供发送HTTP请求的能力。要调用外部API,你需要使用专门的HTTP客户端库,比如axios、request(虽然已弃用,但很多老项目还在使用)或者node-fetch。
另外还有个小问题:你的smsService.sendSms回调里的res参数和外层Express的响应对象res重名了,这会导致变量遮蔽,后续如果要操作Express的响应对象可能会出问题,建议调整参数名或者不要直接传递res到服务层。
修复方案
我们以目前最推荐的axios为例来修改代码:
第一步:安装axios
在项目根目录执行:
npm install axios
第二步:修改smsService.js
替换掉原来的req.post逻辑,用axios来发送POST请求,同时优化参数传递(只传需要的req.body而不是整个req,职责更清晰):
var axios = require('axios'); var smsService = {}; // 注意:这里不再传递Express的req/res,只传需要的请求体数据 smsService.sendSms = (requestBody, fCallback) => { const sUrl = 'http://smses.io/api-send-sms.php'; const sToken = '$2y$10$//Qx9DsrDCIeNeWIjr1V.uWRR3m9raVGJNN4iDRZsxNDxknvCJsPC'; let sRecipientNbr = requestBody.mobile_number; let sMessage = '5678'; console.log('CALLING SMS API...', sRecipientNbr); // 用axios发送POST请求,注意API需要的表单格式,用URLSearchParams包装 axios.post(sUrl, new URLSearchParams({ action: 'SEND', mobile: sRecipientNbr, message: sMessage, apiToken: sToken, })) .then((response) => { var jResponse = safeJsonParse(response.data); console.log('SMS API return:', jResponse); if (jResponse.status !== 'ok') { console.error('SMS API RETURNED ERROR', jResponse); return fCallback({ error: true, message: 'System is currently unavailable' }); } return fCallback(false); }) .catch((err) => { console.log('ERROR WHEN CALLING SMS API', err); return fCallback({ error: true, message: 'System is currently unavailable' }); }); }; module.exports = smsService;
第三步:修改server.js中的调用
调整smsService.sendSms的调用参数,只传递req.body:
app.post('/signup-user', upload.single('avatar'), (req, res) => { let sAvatarImagePath = req.file.path.split("public/").pop() let token = jwt.sign({ username: req.body.email }, 'supersecret', { expiresIn: 240 }); user.createUser(req.body, sAvatarImagePath, (err, jResult) => { if (err) { return res.send(jResult) } mailer.sendEmail(res, req.body, token) // 只传递req.body,由路由层处理响应 smsService.sendSms(req.body, (err, jResult) => { if (err) { return res.send(jResult) } }) }) })
可选:如果坚持使用回调风格的request库
如果你习惯用回调而不是Promise,可以使用request库(虽然已弃用,但暂时可以用):
- 安装:
npm install request - 修改
smsService.js:
var request = require('request'); var smsService = {}; smsService.sendSms = (requestBody, fCallback) => { const sUrl = 'http://smses.io/api-send-sms.php'; const sToken = '$2y$10$//Qx9DsrDCIeNeWIjr1V.uWRR3m9raVGJNN4iDRZsxNDxknvCJsPC'; let sRecipientNbr = requestBody.mobile_number; let sMessage = '5678'; console.log('CALLING SMS API...', sRecipientNbr); request.post({ url: sUrl, form: { action: 'SEND', mobile: sRecipientNbr, message: sMessage, apiToken: sToken, } }, (err, response, body) => { if (err) { console.log('ERROR WHEN CALLING SMS API', err); return fCallback({ error: true, message: 'System is currently unavailable' }); } var jResponse = safeJsonParse(body); console.log('SMS API return:', jResponse); if (jResponse.status !== 'ok') { console.error('SMS API RETURNED ERROR', jResponse); return fCallback({ error: true, message: 'System is currently unavailable' }); } return fCallback(false); }); }; module.exports = smsService;
这样修改后,就能解决req.post is not a function的错误,同时代码的职责划分更清晰,也避免了变量遮蔽的问题。
内容的提问来源于stack exchange,提问作者July333

