使用botbuilder-viber发送消息时触发ERR_INVALID_ARG_TYPE错误
先梳理下你的问题背景和细节:
相关模块
botbuilder-viber
平台信息
- 操作系统:Ubuntu
- Node版本:9.3.0
- NPM版本:5.5.1
代码示例
var viber = require('botbuilder-viber'); var express = require('express'); var bodyParser = require('body-parser'); var viberOptions = { Token: '***', Name: '***', AvatarUrl: '***' } var viberChannel = new viber.ViberEnabledConnector(viberOptions); var expressApp = express(); expressApp.listen(process.env.port || process.env.PORT || 3978, function() { console.log("Express server is running."); }); expressApp.use(bodyParser.urlencoded({ extended: false })); expressApp.use(bodyParser.json()); bot.connector(viber.ViberChannelId, viberChannel); expressApp.use('/viber/webhook', viberChannel.listen());
预期行为
服务运行无错误,正常处理Viber机器人消息
实际行为
每次从Viber发送消息时,都会重复触发两次以下错误:
TypeError [ERR_INVALID_ARG_TYPE]: The "data"参数必须为string、TypedArray或DataView类型
at Hmac.update (internal/crypto/hash.js:53:11)
at MessageValidator._calculateHmacFromMessage (/app/node_modules/viber-bot/lib/message/message-validator.js:17:54)
at MessageValidator.validateMessage (/app/node_modules/viber-bot/lib/message/message-validator.js:11:30)
at _app.use (/app/node_modules/viber-bot/lib/middleware.js:61:32)
at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/app/node_modules/express/lib/router/index.js:317:13)
at /app/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/app/node_modules/express/lib/router/index.js:335:12)
at next (/app/node_modules/express/lib/router/index.js:275:10)
at textParser (/app/node_modules/body-parser/lib/types/text.js:60:7)
at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/app/node_modules/express/lib/router/index.js:317:13)
at /app/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/app/node_modules/express/lib/router/index.js:335:12)
at next (/app/node_modules/express/lib/router/index.js:275:10)
at expressInit (/app/node_modules/express/lib/middleware/init.js:40:5)
at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/app/node_modules/express/lib/router/index.js:317:13)
at /app/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/app/node_modules/express/lib/router/index.js:335:12)
at next (/app/node_modules/express/lib/router/index.js:275:10)
at query (/app/node_modules/express/lib/middleware/query.js:45:5)
at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/app/node_modules/express/lib/router/index.js:317:13)
at /app/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/app/node_modules/express/lib/router/index.js:335:12)
at next (/app/node_modules/express/lib/router/index.js:275:10)
at Function.handle (/app/node_modules/express/lib/router/index.js:174:3)
问题根源
这个错误的核心原因是:Viber的消息签名验证需要读取原始的HTTP请求体(字符串格式),但你的代码提前用body-parser把请求体解析成了JSON对象。当viber-bot库尝试用解析后的对象去计算HMAC签名时,就会触发Hmac.update()的类型错误——它只接受字符串、TypedArray或DataView类型的数据。
至于你看到重复两次错误,大概率是Viber服务器在第一次验证失败后会重试一次请求,导致错误被触发两次。
解决方案
你需要调整中间件的执行顺序,让/viber/webhook路由保留原始请求体,这里提供两种可行的方案:
方案1:为Viber webhook单独配置原始数据中间件
在body-parser之前,先为/viber/webhook添加express.raw()中间件,确保该路由保留原始请求体:
var viber = require('botbuilder-viber'); var express = require('express'); var bodyParser = require('body-parser'); var viberOptions = { Token: '***', Name: '***', AvatarUrl: '***' } var viberChannel = new viber.ViberEnabledConnector(viberOptions); var expressApp = express(); expressApp.listen(process.env.port || process.env.PORT || 3978, function() { console.log("Express server is running."); }); // 先为viber webhook配置原始数据解析 expressApp.use('/viber/webhook', express.raw({ type: 'application/json' })); // 再为其他路由配置普通的body解析 expressApp.use(bodyParser.urlencoded({ extended: false })); expressApp.use(bodyParser.json()); bot.connector(viber.ViberChannelId, viberChannel); expressApp.use('/viber/webhook', viberChannel.listen());
方案2:排除Viber webhook路径不被body-parser解析
如果你不想用raw中间件,可以让body-parser跳过/viber/webhook路径:
var viber = require('botbuilder-viber'); var express = require('express'); var bodyParser = require('body-parser'); var viberOptions = { Token: '***', Name: '***', AvatarUrl: '***' } var viberChannel = new viber.ViberEnabledConnector(viberOptions); var expressApp = express(); expressApp.listen(process.env.port || process.env.PORT || 3978, function() { console.log("Express server is running."); }); // 只对非/viber/webhook的路径使用body-parser expressApp.use((req, res, next) => { if (req.path !== '/viber/webhook') { bodyParser.urlencoded({ extended: false })(req, res, next); bodyParser.json()(req, res, next); } else { next(); } }); bot.connector(viber.ViberChannelId, viberChannel); expressApp.use('/viber/webhook', viberChannel.listen());
验证方法
修改代码后重启服务,再从Viber发送消息测试,应该就不会再出现这个类型错误了。
内容的提问来源于stack exchange,提问作者Alexey Mikhailov




