You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用botbuilder-viber发送消息时触发ERR_INVALID_ARG_TYPE错误

解决botbuilder-viber触发重复Hmac数据类型错误的问题

先梳理下你的问题背景和细节:

相关模块

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

火山引擎 最新活动