根据编码的 formData 值设置节点请求 header

Set node request header based on encoded formData value

我正在尝试使用 request npm 包将 HTTP POST 发送到需要特殊 header 的 API,其中包含一个 base64请求 body 的字符串表示的编码 MD5 散列。

生成 MD5 哈希的代码:

function md5(val) {
    val = val || '';
    return crypto.createHash('md5').update(val).digest('base64');
}

当没有 formData 时,对于 GET 或 DELETE 请求,我应该使用的值是一个空字符串,这很好用。 API 接受 header 和 returns 请求的数据。

不幸的是,当使用 formData 选项发布文件时,object 被 request 模块编码。因此,当服务器将我的 MD5 哈希值与他们端收到的 body 进行比较时,它不匹配并抛出错误。

我需要的简化请求:

var formData = {
    left: 0,
    top: 0,
    width: 0,
    height: 0,
    profileImage: fs.readFileSync(__dirname + '/test_image.jpg')
};

var reqOptions = {
    url: 'https://example.com/user/1234/profile-image',
    method: 'POST,
    json: true,
    headers: {
        'Content-MD5': md5(formData)
    },
    formData: formData
}

request(reqOptions, function(err, response, body) {
    //process the response...
});

上面的例子会抛出一个错误,因为 formData 变量是一个 object 而 crypto 模块需要一个字符串。我开始手动编写代码将 formData object 转换为字符串,但是在 request 已经完成的情况下重写所有编码所有表单值的逻辑似乎有点荒谬模块。

我正在寻找一种可靠的方法来获取 精确 编码的表单内容,在它们被 request 模块处理之后,但在请求之前实际上已发送,因此我可以构建哈希值并添加 header.

request 使用 form-data npm 包对表单提交进行编码。可通过 request.form() 访问该对象。您应该能够获取它并将其通过管道传输到 md5 哈希实例中:

var formData = r.form();
var hash = crypto.createHash('md5');
hash.setEncoding('hex');
form.on('end', function() {
    hash.end();
    console.log('hash is', hash.read());
    // submit request here
});

如果您将 form 流式传输到 md5 哈希中,然后 request 尝试再次使用同一流,则可能会出现问题。在这种情况下,需要、实例化和填充 form-data 自己来计算散列。然后创建 form-data 的另一个实例并将其传递给 request.

您可以手动执行此操作,例如:

var crypto = require('crypto');
var FormData = require('form-data');

var form = new FormData();
form.append('left', 0);
form.append('top', 0);
form.append('width', 0);
form.append('height', 0);
form.append('profileImage', fs.readFileSync(__dirname + '/test_image.jpg'));

var rawChunks = [];
var hash = crypto.createHash('md5');
form.on('data', function(chunk) {
  rawChunks.push(chunk);
  hash.update(chunk);
}).on('end', function() {
  var headers = form.getHeaders();
  headers['Content-MD5'] = hash.digest('base64');
  var req = request({
    url: 'https://example.com/user/1234/profile-image',
    method: 'POST',
    headers: headers
  }, function(err, res, body) {
    // Do something with response
  });
  for (var i = 0; i < rawChunks.length; ++i)
    req.write(rawChunks[i]);
  req.end();
});

另一种可能的替代方法是使用分块编码并将 Content-MD5 作为 HTTP 尾部 header 传递(header 出现在 body 之后)。这将允许您防止在内存中缓冲生成的表单数据(如果您决定将 fs.readFileSync() 更改为 fs.createReadStream(),甚至可能在内存中缓冲文件字段)。然而,这完全取决于目标服务器是否支持预告片 headers(无论是解析它们还是实际对它们进行处理)。