中间件

中间件 (也叫 pre 和 post 钩子) 是函数 which are passed control during execution of asynchronous functions. Middleware is specified on the schema level and is useful for writing plugins. Mongoose 4.0 has 2 types of middleware: 文档中间件和查询中间件.

文档中间件支持如下文档函数.

查询中间件支持如下模型和查询函数.

Both document middleware and query middleware support pre and post hooks. How pre and post hooks work is described in more detail below.

注意:There is no query hook forremove(), only for documents. If you set a 'remove' hook, it will be fired when you callmyDoc.remove(), not when you callMyModel.remove().

注意:Thecreate()function firessave()hooks.

Pre

There are two types ofprehooks, serial and parallel.

序列

Serial middleware are executed one after another, when each middleware callsnext.

var schema = new Schema(..);
schema.pre('save', function(next) {
  // do stuff
  next();
});

平行

Parallel middleware offer more fine-grained flow control.

var schema = new Schema(..);

// `true` means this is a parallel middleware. You **must** specify `true`
// as the second parameter if you want to use parallel middleware.
schema.pre('save', true, function(next, done) {
  // calling next kicks off the next middleware in parallel
  next();
  setTimeout(done, 100);
});

The hooked method, in this casesave, will not be executed untildoneis called by each middleware.

用例

Middleware are useful for atomizing model logic and avoiding nested blocks of async code. Here are some other ideas:

  • complex validation
  • removing dependent documents
    • (removing a user removes all his blogposts)
  • asynchronous defaults
  • asynchronous tasks that a certain action triggers
    • triggering custom events
    • notifications

错误控制

If any middleware callsnextordonewith a parameter of typeError, the flow is interrupted, and the error is passed to the callback.

schema.pre('save', function(next) {
  // You **must** do `new Error()`. `next('something went wrong')` will
  // **not** work
  var err = new Error('something went wrong');
  next(err);
});

// later...

myDoc.save(function(err) {
  console.log(err.message) // something went wrong
});

提交中间件

post middleware are executed after the hooked method and all of itspremiddleware have completed.postmiddleware do not directly receive flow control, e.g. nonextordonecallbacks are passed to it.posthooks are a way to register traditional event listeners for these methods.

schema.post('init', function(doc) {
  console.log('%s has been initialized from the db', doc._id);
});
schema.post('validate', function(doc) {
  console.log('%s has been validated (but not saved yet)', doc._id);
});
schema.post('save', function(doc) {
  console.log('%s has been saved', doc._id);
});
schema.post('remove', function(doc) {
  console.log('%s has been removed', doc._id);
});

异步提交钩子

While post middleware doesn't receive flow control, you can still make sure that asynchronous post hooks are executed in a pre-defined order. If your post hook function takes at least 2 parameters, mongoose will assume the second parameter is anext()function that you will call to trigger the next middleware in the sequence.

// Takes 2 parameters: this is an asynchronous post hook
schema.post('save', function(doc, next) {
  setTimeout(function() {
    console.log('post1');
    // Kick off the second post hook
    next();
  }, 10);
});

// Will not execute until the first middleware calls `next()`
schema.post('save', function(doc, next) {
  console.log('post2');
  next();
});

Save/Validate 钩子

Thesave()function triggersvalidate()hooks, because mongoose has a built-inpre('save')hook that callsvalidate(). This means that allpre('validate')andpost('validate')hooks get calledbeforeanypre('save')hooks.

schema.pre('validate', function() {
  console.log('this gets printed first');
});
schema.post('validate', function() {
  console.log('this gets printed second');
});
schema.pre('save', function() {
  console.log('this gets printed third');
});
schema.post('save', function() {
  console.log('this gets printed fourth');
});

注意 findAndUpdate() 和 查询中间件

update(),findOneAndUpdate(),等里Pre 和 postsave()钩子 不执行. 更多相关讨论请查看 这个 GitHub issue. Mongoose 4.0 给这些函数专有钩子.

schema.pre('find', function() {
  console.log(this instanceof mongoose.Query); // true
  this.start = Date.now();
});

schema.post('find', function(result) {
  console.log(this instanceof mongoose.Query); // true
  // prints returned documents
  console.log('find() returned ' + JSON.stringify(result));
  // prints number of milliseconds the query took
  console.log('find() took ' + (Date.now() - this.start) + ' millis');
});

Query middleware differs from document middleware in a subtle but important way: in document middleware,thisrefers to the document being updated. In query middleware, mongoose doesn't necessarily have a reference to the document being updated, sothisrefers to thequeryobject rather than the document being updated.

For instance, if you wanted to add anupdatedAttimestamp to everyupdate()call, you would use the following pre hook.

schema.pre('update', function() {
  this.update({},{ $set: { updatedAt: new Date() } });
});

错误控制中间件

New in 4.5.0

Middleware execution normally stops the first time a piece of middleware callsnext()with an error. However, there is a special kind of post middleware called "error handling middleware" that executes specifically when an error occurs.

Error handling middleware is defined as middleware that takes one extra parameter: the 'error' that occurred as the first parameter to the function. Error handling middleware can then transform the error however you want.

var schema = new Schema({
  name: {
    type: String,
    // Will trigger a MongoError with code 11000 when
    // you save a duplicate
    unique: true
  }
});

// Handler **must** take 3 parameters: the error that occurred, the document
// in question, and the `next()` function
schema.post('save', function(error, doc, next) {
  if (error.name === 'MongoError' && error.code === 11000) {
    next(new Error('There was a duplicate key error'));
  } else {
    next(error);
  }
});

// Will trigger the `post('save')` error handler
Person.create([{ name: 'Axl Rose' }, { name: 'Axl Rose' }]);

Error handling middleware also works with query middleware. You can also define a postupdate()hook that will catch MongoDB duplicate key errors.

// The same E11000 error can occur when you call `update()`
// This function **must** take 3 parameters. If you use the
// `passRawResult` function, this function **must** take 4
// parameters
schema.post('update', function(error, res, next) {
  if (error.name === 'MongoError' && error.code === 11000) {
    next(new Error('There was a duplicate key error'));
  } else {
    next(error);
  }
});

var people = [{ name: 'Axl Rose' }, { name: 'Slash' }];
Person.create(people, function(error) {
  Person.update({ name: 'Slash' }, { $set: { name: 'Axl Rose' } }, function(error) {
    // `error.message` will be "There was a duplicate key error"
  });
});

Next Up

Now that we've covered middleware, let's take a look at Mongoose's approach to faking JOINs with its querypopulationhelper.

results matching ""

    No results matching ""