我有一个nodejs代理来调用服务。在响应上,请求通过管道传输到服务 url(如果您想在返回响应之前解析响应,我想这是正确的方法(。问题是解析器有时会在 JSON.parse(data( 上失败,因为它 意外的输入结束。从我在调试时看到的问题是正在解析的数据不完整(即使服务正确返回它(。我对管道和流没有太多经验,所以我不确定为什么有时会失败。
//Request setup
r.on('response', function(resp) {
if (resp.statusCode === 200) {
r.pipe(responseParser(config.get('service:url'))).pipe(res);
} else {
r.pipe(res);
}
});
//Parser module
var _ = require('lodash'),
stream = require('stream');
module.exports = function responseParser(url) {
var data = '',
parser = new stream.Transform({
objectMode: true
});
parser._transform = function (chunk, encoding, done) {
data += chunk.toString();
done();
};
parser._flush = function (done) {
if (data) {
var obj = mapValues(JSON.parse(data));
this.push(JSON.stringify(obj));
}
done();
};
function mapValues(data){
...
}
return parser;
}
我仍然不知道为什么有时在返回所有数据块之前调用刷新,但我为了避免这种情况所做的只是在块到达时解析它们,通过确保在一个块中我不会获得我需要映射的值的部分数据。如果一个块只包含目标值的部分信息,我会删除它,并将其添加到下一个块的开头。这样,数据在传入时就会被解析,这样我就不必依赖于仅在所有数据返回时才调用刷新的事实。
我会禁用objectMode
,因为在这种情况下没有必要。此外,您需要将 JSON 解析包装在 try-catch 中,以防输入格式不正确:
module.exports = function responseParser(url) {
var data = '';
var parser = new stream.Transform();
parser._transform = function(chunk, encoding, done) {
data += chunk;
done();
};
parser._flush = function(done) {
var err;
if (data) {
try {
var obj = mapValues(JSON.parse(data));
this.push(JSON.stringify(obj));
this.push(null);
} catch (ex) {
err = ex;
}
}
done(err);
};
function mapValues(data){
// ...
}
return parser;
};
您可能还想先检查resp.headers['content-type']
是否包含application/json
,然后再尝试对其进行解析,并且您可能希望创建自定义 Transform 子类并将其实例化,而不是每次都创建新的_transform()
和_flush()
函数。
与其自己编写,不如使用知道如何解析流的流式 JSON 解析器?例如,JSONStream。
让您的生活更轻松的另一种选择是使用流到承诺将读取流转换为承诺,该承诺将解析为 JSON 的缓冲区,然后您可以解析该缓冲区。
另外,为什么您的代理要解析 JSON?