Установить заголовок запроса узла на основе закодированного значения 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
,