Как правильно обрабатывать ошибки (строковые ИЛИ объекты) в express.js с помощью обещаний

Я не в своем первом приложении express.js, хотя мне еще предстоит найти самый надежный способ обработки ошибок.

Поскольку io.js стал реальностью за пару месяцев, я использую встроенные обещания, чтобы помочь себе с асинхронностью, следующий код отражает это.

Мое промежуточное программное обеспечение для обработки ошибок выглядит следующим образом:

router.use(function (err, req, res, next) {
  // in case err.custom is present, means is an "handled" Error, created by developers
  if (!!err.custom) {
    return res.status(err.code).json(err.message);
  }

  if (err instanceof Error) {
    console.error(err.stack);
    return res.status(500).send('Runtime Error'); // should be reported!
  }
  // last but not least, validation error
  res.status(400).send(err);
});

Пример контроллера написан так:

function callService1 (param1) {
  return new Promise(function (resolve, reject) {
    service.call(param1, function (err, data) {
      if (!!err) return reject(err); // this is an Error object?? not sure!

      if (!!data.handledError) { // this is an handled Error to the user, not 500
        return reject({ custom: true, status: 403, message: 'service1 tells you that myCoolParam is not valid' });
      }
      resolve(data);
    });
  };
}

function callService2 (dataFromParam1) {
  return new Promise(function (resolve, reject) {
    // something here
  });
}

// this is the API "controller"
module.exports = function (req, res, next) {
  callService1(req.body.myCoolParam)
  .then(callService2)
  .then(function (service2Output) {
    res.status(200).json({ message: 'everything went smooth!' });
  })
  .catch(next); // here is the catch-all errors
};

Как видите, экспресс-связующее ПО выглядит довольно аккуратно и элегантно.
Я обычно обрабатываю все интересные ошибки для пользователя в rejects()некоторые из них вызываются с объектом, где я сообщаю промежуточное ПО обработки ошибок.

Проблема в service из примера сторонняя библиотека. Эти библиотеки иногда возвращают строку, иногда объект (из внешнего API), иногда ошибку JavaScript.

В настоящее время я не могу обрабатывать пользовательские объекты javascript, более того, если я хочу выдать ошибку 500 пользователю, который должен сделать reject(new Error(err)); но иногда это err является объектом, в результате чего:

Error: [object Object]
    at errorHandler (awesomeapi\postsomething.js:123:16)
    at IncomingMessage.<anonymous> (node_modules\mandrill-api\mandrill.js:83:24)
    at emitNone (events.js:72:20)
    at IncomingMessage.emit (events.js:163:7)
    at _stream_readable.js:891:16
    at process._tickCallback (node.js:337:11)

Это совсем не круто, и мне бы очень хотелось найти способ изящно обрабатывать эти ошибки без добавления кода (если это возможно), поскольку я считаю этот синтаксис довольно элегантным и лаконичным.

1 ответ

Решение

Я много думал об этой проблеме, и в итоге я создал / использовал этот https://github.com/yzarubin/x-error/blob/master/lib/x-error.js Это пользовательская сторона сервера объект error, который наследует Error и расширяет функциональность для обработки http-кодов и ответов.

Чтобы применить это в вашем случае, я бы сделал что-то вроде этого:

function callService1 (param1) {
  return new Promise(function (resolve, reject) {
    service.call(param1, function (err, data) {
      if (!!err) return reject(new xError().extend(err).setHttpCode(500)); // This will inherit the original stack & message

      if (!!data.handledError) { 
        return reject(new xError('this is an handled Error to the user, not 500').setHttpCode(403));
      }
      resolve(data);
    });
  };
}

Затем в вашем контроллере вы можете проверить instanceof xError === true и обработать его, иначе сделать какой-то ответ по умолчанию. Но я также сделал что-то подобное в приложении, где было уверено, что каждое обещание в конечном итоге либо разрешит, либо отклонит экземпляр xError:

router.use(function (err, req, res, next) {
  res.status(err.httpCode || 500).send(err.message || 'Internal error');
});
Другие вопросы по тегам