网站搜索

javascript - 如何从异步 mongoose 中间件的 post 钩子抛出错误


从异步 Mongoose 中间件 post 钩子 抛出错误的正确方式是什么?

代码示例

以下 TypeScript 代码使用 mongoose 的 post init 事件来运行一些检查,这些检查会在每当函数从 mongoDb 获取文档时触发。这个示例中的 postInit() 函数正在执行一些后台检查。它在某些情况下会失败,然后返回 Promise.reject('Error!');

schema.post('init', function (this: Query<any>, doc: any) {
    return instance.postInit(this, doc) 
    .catch( err => {
        return err;
    });
});

钩子运行良好。例如,以下代码会触发钩子:

MyMongooseModel.findOne({ _id : doc.id}, (err, o : any) => {
    console.log(o);
});

然而,如果 postInit() 失败,错误不会传回调用函数。相反,将返回文档。

预期行为

我正在寻找将此错误传递给调用函数的正确方法。如果后台检查失败,调用函数不应该获取到文档。

我尝试了不同的方法来引发这个错误。例如:throw new Error('Error');。然而,这会导致 UnhandledPromiseRejectionWarning,并且仍然返回文档。

答案#1

我是 Mongoose 的维护者。不幸的是,init() 钩子是同步的,而我们在文档中对此说明得不够好。我们已经在 GitHub 上开了一个 issue,并将尽快添加相关文档。在 post('init') 中报告错误的唯一方法是 throw 它。

const assert = require('assert');
const mongoose = require('mongoose');
mongoose.set('debug', true);

const GITHUB_ISSUE = `init`;
const connectionString = `mongodb://localhost:27017/${ GITHUB_ISSUE }`;
const { Schema } = mongoose;

run().then(() => console.log('done')).catch(error => console.error(error.stack));

async function run() {
  await mongoose.connect(connectionString);
  await mongoose.connection.dropDatabase();

  const schema = new mongoose.Schema({
    name: String
  });
  schema.post('init', () => { throw new Error('Oops!'); });

  const M = mongoose.model('Test', schema);

  await M.create({ name: 'foo' });

  await M.findOne(); // Throws "Oops!"
}

这是因为 Mongoose 假设 init() 在内部是同步的。

答案#2

在这个post init hook方法中,你只会收到一个doc:

Document.prototype.init()

参数 doc «Object» 由 mongo 返回的文档 初始化文档而不使用 setter 或标记任何内容已修改。

在文档从mongodb返回后内部调用。

Mongoose 文档:初始化钩子文档

要触发一个错误,你需要一个 done 或 next 方法:

帖子中间件

后置中间件在钩住的方法及其所有前置中间件完成后执行。后置中间件不会直接接收流程控制,例如,不会传递 next 或 done 回调给它。后置钩子是一种为这些方法注册传统事件监听器的方式。

Mongoose 文档:后置中间件

如果你只想知道你的调用中是否发生了错误,请改成这个:

MyMongooseModel.findOne({ _id : doc.id}, (err, o : any) => {
      if(err) {
        throw new Error(err);
      }

      console.log(o);
    });

如果你想传播错误,一种选择是使用 pre 钩子方法:

schema.pre('save', function(next) {
  const err = new Error('something went wrong');
  // If you call `next()` with an argument, that argument is assumed to be
  // an error.
  next(err);
});

schema.pre('save', function() {
  // You can also return a promise that rejects
  return new Promise((resolve, reject) => {
    reject(new Error('something went wrong'));
  });
});

schema.pre('save', function() {
  // You can also throw a synchronous error
  throw new Error('something went wrong');
});

schema.pre('save', async function() {
  await Promise.resolve();
  // You can also throw an error in an `async` function
  throw new Error('something went wrong');
});

示例错误处理:错误处理