mongoose 文檔(六) Middleware

中間件(也稱爲pre and post hook)是執行異步函數期間傳遞控制權的函數。中間件在schema級別上被指定並對於編寫插件很是有用。Mongoose 4.0有兩種中間件:document 中間件和query 中間件。document中間件支持如下document 函數。html

 query中間件支持如下model和query函數。api

document中間件和query中間件都支持 pre hooks 和 post hooks。下面詳細描述pre hooks 和 post hooks如何工做。異步

注意:沒有對remove()的query hook,只有對document的。若是你設置了一個remove hook,執行myDoc.remove()時它會激活,而不是執行MyModel.remove()。async

 

Pre

有2種類型的pre hook,串行(seria)和並行(parallel)。mongoose

Serial

串行中間件是一個接一個執行,每一箇中間件調用next。函數

var schema = new Schema(..);
schema.pre('save', function(next) {
  // 作些什麼
  next();
});

Parallel

並行中間件提供更細粒度的操做post

var schema = new Schema(..);

// 'true'表示這是一個並行中間件. 若是你想要使用並行中間件,你必須指定true做爲第二個參數 
schema.pre('save', true, function(next, done) {
  // 下一個要執行的中間件並行執行
  next();
  setTimeout(done, 100);
});

在這種狀況下,hooked方法直到每一箇中間件都調用了done纔會執行保存。this

用例

中間件有用於霧化model邏輯和避免異步代碼嵌套。這裏有一些其餘的例子:spa

  • 複雜驗證
  • 刪除相關document,如刪除用戶也刪除了他全部的博客文章
  • 異步缺省
  • 某個特定動做觸發異步任務,例如觸發自定義事件和通知

錯誤處理

若是任何一箇中間件調用next或done 處理錯誤實例,流程會被阻止,而且該錯誤被傳遞給回調。插件

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 中間件

post中間件在hooked方法和全部它的pre中間件完成後才執行。post中間件不直接接收流控制,如沒有next和done回調都傳遞給它。post hook可以爲這些方法註冊傳統的事件監聽器。

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);
});

 

 

異步Post hook

雖然post中間件不接受流控制,你仍然可以確保異步post hook能按預先定義的順序執行。若是你的post hook方法至少須要2個參數,mongoose會假設第二個參數是你將調用的next()函數來依次觸發next中間件

// 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 Hooks

 save()函數觸發validate() hook,由於mongoose 有一個內置的pre('save') hook 執行validate()。這意味着全部pre('validate') 和post('validate') hook在任何pre('save') hook前被調用。

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');
});

 

 

Notes on findAndUpdate() 和 Query中間件

Pre 和 post save() hooks 不在update()、findOneAndUpdate()等執行。 想知道爲何你能夠看這個GitHub問題。Mongoose 4.0 對這些函數有清楚的hook。

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中間件與document中間件在一個微秒而重要的地方不一樣:在document中間件,這是指正在更新的document。在query中間件,mongoose不必定與document更新有關,所以這是指query對象而不是更新document。

 

例如,若是你想要增長一個updatedAt時間戳給每一個update(),你要使用下面的pre hook。

schema.pre('update', function() {
  this.update({},{ $set: { updatedAt: new Date() } });
});
相關文章
相關標籤/搜索