Возврат из экспресс-маршрута изнутри обратного вызова мангуста при ошибке

#node.js #express #mongoose

Вопрос:

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

 router.route('/addSomething').post(async (req,res,next) => {
  await Document.findOne(
    {name: req.body.name}, // search parameter
    (error,doc) = { // callback
      if(error) return next(error) // pass system generated error to next() and exit route
      else if (doc) return next(new Error("This name already exists")) // pass my own error to next() and exit route
    })

  // everything from now on shouldn't happen

  await Document.save() etc...
 

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

Я ищу элегантный способ обработки ошибок mongoose/mongodb и выхода из функции в момент ошибки, не помещая блоки try/catch повсюду.

Ответ №1:

Когда вы возвращаетесь next(...) в свою функцию обратного вызова, вы на самом деле возвращаетесь не из остальной части своего кода, а только внутри этой функции. Чтобы устранить эту проблему, я добавил continue() функцию внутри вашего кода для продолжения входящего HTTP-запроса.

 router.route('/addSomething').post(async (req, res, next) => {
    await Document.findOne(
        { name: req.body.name }, // search parameter
        (error, doc) => { // callback
            if (error) return next(error) // pass system generated error to next() and exit route
            else if (doc) return next(new Error("This name already exists")) // pass my own error to next() and exit route

            continue(); // Call the continue function if function isn't already returned
        }
    );
    function continue() { // Add a continue function
        // Continue your code
        await Document.save();
        // ...
    }
});
 

Комментарии:

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

2. @EdwardMartin Верно, однако единственный другой способ сделать это-обернуть весь ваш код в обратный вызов, тогда вы вызовете ад обратного вызова.

Ответ №2:

Рассмотрите возможность использования util.promisify , чтобы избавиться от обратного вызова:

 router.route('/addSomething').post(async (req, res, next) => {
  try {
    var doc = await util.promisify(Document.findOne)(
      { name: req.body.name } // search parameter
    );
  } catch(error) {
    return next(error); // pass system generated error to next() and exit route
  }
  if (doc) return next(new Error("This name already exists")); // pass my own error to next() and exit route
  // everything from now on shouldn't happen
  await Document.save() etc...
});
 

Комментарии:

1. Спасибо за ваше предложение. Document.findOne() уже возвращает обещание. Я могу использовать . затем и .методы улова, но предпочел бы не

2. Если вы используете await , вам не нужно использовать .then и .catch . Строка 3 моего ответа тогда просто становится var doc = await Document.findOne( .

3. Тогда вопрос в том, как я могу обработать любую ошибку из этого, не заключая ее в блок try/catch. В идеале я мог бы сказать var [ошибка,doc] = ожидание документа.findOne( аналогично обратному вызову, но я знаю, что это не сработает!

4. Не вижу, как вы могли бы справиться с ошибкой, если бы не использовали ни catch то, ни .catch другое .

5. Они могут быть обработаны с помощью обратного вызова findOne: .findOne({},(ошибка,документ)=>…), но проблема в том, что я не могу выйти из внешней функции в рамках этого обратного вызова