Установить заголовок запроса узла на основе закодированного значения formData

Я пытаюсь отправить HTTP POST, используя request пакет npm для API, для которого требуется специальный заголовок, состоящий из закодированного в base64 хэша MD5 строкового представления тела запроса.

Код для генерации хеша MD5:

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

Когда нет formData, для запроса GET или DELETE значение, которое я должен использовать, является пустой строкой, которая прекрасно работает. API принимает заголовок и возвращает запрошенные данные.

К сожалению, при публикации файла, используя formData опция, объект кодируется request модуль. Таким образом, когда сервер сравнивает мой хэш MD5 с телом, полученным на их конце, он не совпадает и выдает ошибку.

Упрощенный запрос того, что мне нужно:

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 является объектом, а crypto Модуль ожидает строку. Я начал вручную писать код для преобразования объекта formData в строку, но кажется немного нелепым переписывать всю логику, которая кодирует все значения формы, когда это уже выполняется request модуль.

Я ищу надежный способ получить точное закодированное содержимое формы, после того как они были обработаны request модуль, но до того, как запрос действительно отправлен, так что я могу построить хэш-значение и добавить заголовок.

2 ответа

Решение

Вы можете сделать это вручную с помощью чего-то вроде:

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 (заголовок, который появляется после тела). Это позволит вам предотвратить буферизацию сгенерированных данных формы в памяти (и, возможно, даже буферизацию файлового поля в памяти, если вы решите изменить fs.readFileSync() в fs.createReadStream()). Однако все это будет зависеть от того, поддерживает ли конечный сервер заголовки трейлера (анализирует их или действительно что-то делает с ними).

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 самостоятельно вычислить хеш. Затем создайте другой экземпляр данных формы и передайте его request,

Другие вопросы по тегам