web-dev-qa-db-fra.com

corps de réponse express

Le titre devrait être assez explicite.

À des fins de débogage, je souhaite qu'express imprime le code et le corps de la réponse pour chaque demande traitée. L'impression du code de réponse est assez simple, mais l'impression du corps de la réponse est plus délicate, car il semble que le corps de la réponse ne soit pas facilement disponible en tant que propriété.

Ce qui suit ne fonctionne pas:

var express = require('express');
var app = express();

// define custom logging format
express.logger.format('detailed', function (token, req, res) {                                    
    return req.method + ': ' + req.path + ' -> ' + res.statusCode + ': ' + res.body + '\n';
});  

// register logging middleware and use custom logging format
app.use(express.logger('detailed'));

// setup routes
app.get(..... omitted ...);

// start server
app.listen(8080);

Bien sûr, je pourrais facilement imprimer les réponses chez le client qui a émis la demande, mais je préférerais également le faire côté serveur.

PS: Si cela peut aider, toutes mes réponses sont JSON, mais j'espère qu'il existe une solution qui fonctionne avec des réponses générales.

37
Rick Deckard

Vous ne savez pas si c'est la solution la plus simple, mais vous pouvez écrire un middleware pour intercepter les données écrites dans la réponse. Assurez-vous de désactiver app.compress().

function logResponseBody(req, res, next) {
  var oldWrite = res.write,
      oldEnd = res.end;

  var chunks = [];

  res.write = function (chunk) {
    chunks.Push(chunk);

    oldWrite.apply(res, arguments);
  };

  res.end = function (chunk) {
    if (chunk)
      chunks.Push(chunk);

    var body = Buffer.concat(chunks).toString('utf8');
    console.log(req.path, body);

    oldEnd.apply(res, arguments);
  };

  next();
}

app.use(logResponseBody);
57
Laurent Perrin

J'ai rencontré un problème en utilisant l'approche proposée par Laurent. Parfois, chunk est une chaîne et pose donc des problèmes lors de l'appel de Buffer.concat (). Quoi qu'il en soit, j'ai trouvé une légère modification corrigée:

function logResponseBody(req, res, next) {
  var oldWrite = res.write,
      oldEnd = res.end;

  var chunks = [];

  res.write = function (chunk) {
    chunks.Push(new Buffer(chunk));

    oldWrite.apply(res, arguments);
  };

  res.end = function (chunk) {
    if (chunk)
      chunks.Push(new Buffer(chunk));

    var body = Buffer.concat(chunks).toString('utf8');
    console.log(req.path, body);

    oldEnd.apply(res, arguments);
  };

  next();
}

app.use(logResponseBody);
11
bartushk

Vous pouvez utiliser express-winston et configurer en utilisant:

expressWinston.requestWhitelist.Push('body');
expressWinston.responseWhitelist.Push('body');

Exemple en coffeescript:

expressWinston.requestWhitelist.Push('body')
expressWinston.responseWhitelist.Push('body')
app.use(expressWinston.logger({
      transports: [
        new winston.transports.Console({
          json: true,
          colorize: true
        })
      ],
      meta: true, // optional: control whether you want to log the meta data about the request (default to true)
      msg: "HTTP {{req.method}} {{req.url}}", // optional: customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}"
      expressFormat: true, // Use the default Express/morgan request formatting, with the same colors. Enabling this will override any msg and colorStatus if true. Will only output colors on transports with colorize set to true
      colorStatus: true, // Color the status code, using the Express/morgan color palette (default green, 3XX cyan, 4XX yellow, 5XX red). Will not be recognized if expressFormat is true
      ignoreRoute: function (req, res) { return false; } // optional: allows to skip some log messages based on request and/or response
    }));
8
Manuel Darveau

Le code accepté ci-dessus a des problèmes avec ES6 . Utilisez le code ci-dessous

function logReqRes(req, res, next) {
  const oldWrite = res.write;
  const oldEnd = res.end;

  const chunks = [];

  res.write = (...restArgs) => {
    chunks.Push(Buffer.from(restArgs[0]));
    oldWrite.apply(res, restArgs);
  };

  res.end = (...restArgs) => {
    if (restArgs[0]) {
      chunks.Push(Buffer.from(restArgs[0]));
    }
    const body = Buffer.concat(chunks).toString('utf8');

    console.log({
      time: new Date().toUTCString(),
      fromIP: req.headers['x-forwarded-for'] || 
      req.connection.remoteAddress,
      method: req.method,
      originalUri: req.originalUrl,
      uri: req.url,
      requestData: req.body,
      responseData: body,
      referer: req.headers.referer || '',
      ua: req.headers['user-agent']
    });

    // console.log(body);
    oldEnd.apply(res, restArgs);
  };

  next();
}

module.exports = logReqRes;
6
SanRam

J'ai en fait fait ce petit npm astucieux pour résoudre ce problème, j'espère que vous l'aimerez!

https://www.npmjs.com/package/morgan-body

 morgan-body in action!

3
SirRodge

J'ai trouvé que la solution la plus simple à ce problème consistait à ajouter une propriété body à l'objet res lors de l'envoi de la réponse, à laquelle le consignateur peut accéder ultérieurement. J'ajoute ceci à mon propre espace de noms que je gère sur les objets req et res pour éviter les conflits de nommage. par exemple. 

res[MY_NAMESPACE].body = ...

J'ai une méthode utilitaire qui formate toutes les réponses à ma réponse API/JSON standardisée, donc l'ajout de cette ligne a exposé le corps de la réponse lorsque la consignation est déclenchée par l'événement onFinished de res.

0
Joel