Написание экспресс промежуточного программного обеспечения для получения необработанного тела запроса до анализа тела.

Я написал промежуточное ПО Express для извлечения необработанного тела запроса и установил его перед body-parser. промежуточное ПО.

Мое пользовательское промежуточное ПО вызывает req.setEncoding('utf8'), но это вызывает следующую ошибку анализатора тела:

Ошибка: не следует задавать кодировку потока

at readStream (/node_modules/body-parser/node_modules/raw-body/index.js:211:17) 
at getRawBody (/node_modules/body-parser/node_modules/raw-body/index.js:106:12)
at read (/node_modules/body-parser/lib/read.js:76:3)
at jsonParser (/node_modules/body-parser/lib/types/json.js:127:5)

Вот мой код:

var express = require('express');
var bodyParser = require('body-parser')

function myMiddleware() {
  return function(req, res, next) {
    req.rawBody = '';
    req.setEncoding('utf8');

    req.on('data', function(chunk) {
      req.rawBody += chunk;
    });

    req.on('end', function() {
      next();
    });
  }
}

var app = express();
app.use(myMiddleware());
app.use(bodyParser.json());

var listener = app.listen(3000, function() {
});

app.get('/webhook/', function (req, res) {
  res.sendStatus(200);
});

Есть ли способ сбросить кодировку? Есть ли другой способ получить необработанное тело, но после него использовать body-parser?


person kiewic    schedule 16.11.2016    source источник
comment
Использовать промежуточное ПО после bodyParser?   -  person nicovank    schedule 16.11.2016
comment
У вас тоже опечатка в res.sendStatu(200);.   -  person doublesharp    schedule 16.11.2016
comment
Вы уверены, что вам нужно установить кодировку?   -  person doublesharp    schedule 16.11.2016
comment
Опечатка @doublesharp исправлена, спасибо!   -  person kiewic    schedule 16.11.2016
comment
@nicovank Если я изменю порядок промежуточного программного обеспечения, мое пользовательское промежуточное программное обеспечение зависнет. Я думаю, это потому, что поток уже израсходован. Я исследую, что это может быть.   -  person kiewic    schedule 16.11.2016
comment
@doublesharp Вы правы, мне, наверное, не нужно звонить setEncoding(), я думал, что мне тоже нужно, потому что без этого приложение зависает. Теперь я понимаю, что тот, кто попытается прочитать тело во второй раз, будет зависать, в данном случае body-parser.   -  person kiewic    schedule 16.11.2016
comment
Правильно, потому что next() не вызывается до тех пор, пока end не будет передано данным. Попробуйте просто установить обработчики событий, а затем вызвать next() в конце, не в обработчике.   -  person doublesharp    schedule 16.11.2016
comment
D'oh, ваше пользовательское промежуточное программное обеспечение неверно. Функция внутри myMiddleware никогда не вызывается, поэтому next никогда не вызывается.   -  person doublesharp    schedule 16.11.2016


Ответы (3)


Оказывается, body-parser имеет параметр verify для вызвать функцию после прочтения тела запроса. Функция получает тело в качестве буфера.

Вот пример:

var express = require('express');
var bodyParser = require('body-parser')

function verifyRequest(req, res, buf, encoding) {
  // The raw body is contained in 'buf'
  console.log( buf.toString( encoding ) );
};

var app = express();
var listener = app.listen(3000);

// Hook 'verifyRequest' with body-parser here.
app.use(bodyParser.json({ verify: verifyRequest }))

app.post('/webhook/', function (req, res) {
  res.status(200).send("done!");
});
person kiewic    schedule 17.11.2016

Вы вызываете next() внутри «готово», что означает, что поток уже использован. Вместо этого настройте обработчик для «данных», затем передайте запрос, используя next(). Событие "done", скорее всего, обрабатывается внутри bodyParser, поэтому после его выполнения у вас есть доступ к req.rawBody. Если бы это было не так, вы бы добавили еще одно промежуточное ПО, которое вызывает next() внутри req.on('done'), чтобы удерживать остальную часть от обработки, пока у вас не будут все данные.

// custom middleware - req, res, next must be arguments on the top level function
function myMiddleware(req, res, next) {
  req.rawBody = '';

  req.on('data', function(chunk) {
    req.rawBody += chunk;
  });

  // call next() outside of 'end' after setting 'data' handler
  next();  
}

// your middleware
app.use(myMiddleware);

// bodyparser
app.use(bodyParser.json())

// test that it worked
function afterMiddleware(req, res, next) {
  console.log(req.rawBody);
  next();  
}

app.use(afterMiddleware);

Если вам нужен доступ к необработанному телу, вы также можете заглянуть в bodyParser.raw(). Это поместит необработанное тело в req.body, так же, как bodyParse.json(), но его можно заставить запускаться условно в зависимости от типа контента — проверьте options.type.

person doublesharp    schedule 16.11.2016
comment
in your example the inner function is never being called да, это так.... функция myMiddleware возвращает функцию, и он вызывает ее позже app.use(myMiddleware()) - person nicovank; 16.11.2016
comment
Вы правы, но он не проходит в req, res, next, поэтому они не выполняются в правильном контексте. - person doublesharp; 16.11.2016
comment
Написание обработчика промежуточного программного обеспечения как внутренней функции позволяет вызывающим сторонам передавать параметры-параметры в промежуточное программное обеспечение во время установки. - person kiewic; 16.11.2016
comment
У меня новая проблема с этим решением, app.get('/', function (req, res) { }) больше не вызывается. - person kiewic; 16.11.2016

Я рекомендую другой подход, так как ваш текущий подход фактически потребляет сообщение и делает невозможным его чтение синтаксическим анализатором тела (и существует множество ошибок в крайних случаях, которые возникают при синхронном вызове next):

app.use(bodyParser.json());
app.use(bodyParser.text({type: '*/*'}));

Это будет читать любой запрос application/json как JSON, а все остальное как текст.

Если у вас должен быть объект JSON в дополнение к тексту, я рекомендую разобрать его самостоятельно:

app.use(bodyParser.text({type: '*/*'}));
app.use(myMiddleware);

function myMiddleware(req, res, next) {
    req.rawBody = req.body;
    if(req.headers['content-type'] === 'application/json') {
        req.body = JSON.parse(req.body);
    }
    next();
}
person tcooc    schedule 16.11.2016
comment
Я думаю, что разобрать его самостоятельно будет выходом. - person kiewic; 16.11.2016