溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何在nodejs中使用next函數(shù)

發(fā)布時(shí)間:2021-03-17 14:59:51 來源:億速云 閱讀:312 作者:Leah 欄目:web開發(fā)

本篇文章給大家分享的是有關(guān)如何在nodejs中使用next函數(shù),小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

Next的作用

我們?cè)诙xexpress中間件函數(shù)的時(shí)候都會(huì)將第三個(gè)參數(shù)定義為next,這個(gè)next就是我們今天的主角,next函數(shù)主要負(fù)責(zé)將控制權(quán)交給下一個(gè)中間件,如果當(dāng)前中間件沒有終結(jié)請(qǐng)求,并且next沒有被調(diào)用,那么請(qǐng)求將被掛起,后邊定義的中間件將得不到被執(zhí)行的機(jī)會(huì)。

何時(shí)使用Next

從上邊的描述我們已經(jīng)知道,next函數(shù)主要是用來確保所有注冊(cè)的中間件被一個(gè)接一個(gè)的執(zhí)行,那么我們就應(yīng)該在所有的中間件中調(diào)用next函數(shù),但有一個(gè)特例,如果我們定義的中間件終結(jié)了本次請(qǐng)求,那就不應(yīng)該再調(diào)用next函數(shù),否則就可能會(huì)出問題,我們來看段代碼

app.get('/a', function(req, res, next) {
  res.send('sucess');
  next();
});

// catch 404 and forward to error handler
app.use(function(req, res, next) {
 console.log(404);
 var err = new Error('Not Found');
 err.status = 404;
 next(err);
});

app.use(function(err, req, res, next) {
 res.status(err.status || 500);
 res.render('error', {
  message: err.message,
  error: {}
 });
});

發(fā)送請(qǐng)求"/a",控制臺(tái)打印日志如下:

404
GET /a 500 6.837 ms - -
Error: Can't set headers after they are sent.
  at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:345:11)

為什么代碼會(huì)拋異常呢,就是因?yàn)槲覀冊(cè)趓es.send之后調(diào)用了next函數(shù),雖然我們本次的請(qǐng)求已經(jīng)被終止,但后邊的404中間件依舊會(huì)被執(zhí)行,而后邊的中間件試圖去向res的headers中添加屬性值,所以就會(huì)拋出上邊的異常。

讀到這你可能會(huì)有個(gè)疑問,如果我不在res.send后邊調(diào)用next函數(shù),那后邊定義的404中間件是不是永遠(yuǎn)都不會(huì)被執(zhí)行到。現(xiàn)在我們刪除res.send后邊next函數(shù)調(diào)用,發(fā)送請(qǐng)求"/xxx",我們就會(huì)發(fā)現(xiàn)404中間件被執(zhí)行了,(ㄒoㄒ),這不是和我們之前說的矛盾了嗎,我們的自定義中間件沒有調(diào)用next,但后邊定義的中間件仍舊被執(zhí)行了,這究竟是為什么呢??磥碇荒芮笾创a了~~~

Next的內(nèi)部機(jī)制

function next(err) {
  ... //此處源碼省略
  // find next matching layer
  var layer;
  var match;
  var route;

  while (match !== true && idx < stack.length) {
   layer = stack[idx++];
   match = matchLayer(layer, path);
   route = layer.route;

   if (typeof match !== 'boolean') {
    // hold on to layerError
    layerError = layerError || match;
   }

   if (match !== true) {
    continue;
   }
   ... //此處源碼省略
  }
 ... //此處源碼省略
  // this should be done for the layer
  if (err) {
    layer.handle_error(err, req, res, next);
  } else {
   layer.handle_request(req, res, next);
  }
 }

上邊就是express中next的源碼,為了更容易說明問題,對(duì)代碼進(jìn)行了刪減。從上邊的源碼可以發(fā)現(xiàn),next函數(shù)內(nèi)部有個(gè)while循環(huán),每次循環(huán)都會(huì)從stack中拿出一個(gè)layer,這個(gè)layer中包含了路由和中間件信息,然后就會(huì)用layer和請(qǐng)求的path就行匹配,如果匹配成功就會(huì)執(zhí)行l(wèi)ayer.handle_request,調(diào)用中間件函數(shù)。但如果匹配失敗,就會(huì)循環(huán)下一個(gè)layer(即中間件)。

現(xiàn)在我們就能解釋上邊提出的問題了,為什么我們的自定義中間件中沒調(diào)用next函數(shù),但后邊的404中間件仍舊會(huì)被執(zhí)行到,因?yàn)槲覀冋?qǐng)求的"/xxx"匹配不到我們注冊(cè)的"/a"路由中間件,所以while循環(huán)會(huì)繼續(xù)往下執(zhí)行,匹配404中間件成功,所以會(huì)執(zhí)行404中間件。

 注意:app.use注冊(cè)的中間件,如果path參數(shù)為空,則默認(rèn)為"/",而path為"/"的中間件默認(rèn)匹配所有的請(qǐng)求。

有一點(diǎn)需要特別指出,其實(shí)我們?cè)诙x路由中間件的時(shí)候函數(shù)的第三個(gè)參數(shù)next和我們定義非路由中間件的函數(shù)的第三個(gè)參數(shù)next不是同一個(gè)next,我們?cè)谏线吙吹降氖欠锹酚芍虚g件的next,而路由中間件的next函數(shù)是這樣的

function next(err) {
  if (err && err === 'route') {
   return done();
  }

  var layer = stack[idx++];
  if (!layer) {
   return done(err);
  }

  if (layer.method && layer.method !== method) {
   return next(err);
  }

  if (err) {
   layer.handle_error(err, req, res, next);
  } else {
   layer.handle_request(req, res, next);
  }
 }

這個(gè)next比上邊的那個(gè)next要簡(jiǎn)單很多,它負(fù)責(zé)同一個(gè)路由的多個(gè)中間件的控制權(quán)的傳遞,并且它會(huì)接收一個(gè)參數(shù)"route",如果調(diào)用next(“route”),則會(huì)跳過當(dāng)前路由的其它中間件,直接將控制權(quán)交給下一個(gè)路由。

最后有必要再說一說next(err),next(err)是如何將控制權(quán)傳遞到錯(cuò)誤處理中間件的,從前邊的代碼我們知道,當(dāng)調(diào)用next(err)是,express內(nèi)部會(huì)調(diào)用layer.handle_error,那我們來看看它的源碼

Layer.prototype.handle_error = function handle_error(error, req, res, next) {
 var fn = this.handle;

 if (fn.length !== 4) {
  // not a standard error handler
  return next(error);
 }

 try {
  fn(error, req, res, next);
 } catch (err) {
  next(err);
 }
};

代碼中的fn就是中間件函數(shù),express會(huì)對(duì)fn的參數(shù)個(gè)數(shù)進(jìn)行判斷,如果參數(shù)個(gè)數(shù)不等于4則認(rèn)為不是錯(cuò)誤處理中間件,則繼續(xù)調(diào)用next(err),這樣就會(huì)進(jìn)入到下一個(gè)中間件函數(shù),繼續(xù)進(jìn)行參數(shù)個(gè)數(shù)判斷,如此方式一直到某個(gè)中間件函數(shù)的參數(shù)個(gè)數(shù)是4,就認(rèn)為找到了錯(cuò)誤處理中間件,然后執(zhí)行此中間件函數(shù)。

以上就是如何在nodejs中使用next函數(shù),小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見到或用到的。希望你能通過這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI