express中間件原理connect

express中間件原理connect

不知道用了express.js的你有沒有這樣的疑問:前端

  • app.use爲何能夠添加一個又一箇中間件?
  • connect是如何區分普通中間件和錯誤中間件的?
  • 中間件處理函數中的next指代的又是什麼?

我簡單看了一下connect源碼,弄清楚了上面的這3個問題。git

app.use爲何能夠添加一個又一箇中間件?

app.use(function middleware1(req, res, next) {
  // middleware 1
  next();
});
app.use(function middleware2(req, res, next) {
  // middleware 2
  next();
});

connect維護了一個中間件棧(middleware stack)github

數據結構:棧(stack)express

每次調用use,都會向這個應用(app)實例的棧(stack)推入一個帶路徑和處理函數的對象。segmentfault

源碼:瀏覽器

function createServer() {
  function app(req, res, next){ app.handle(req, res, next); }
  // ...
  app.stack = []; // 注意這裏
  return app;
}
proto.use = function use(route, fn) {
  var handle = fn;
  var path = route;
  // ...
  // add the middleware
  this.stack.push({ route: path, handle: handle });

  return this;
};

connect是如何區分普通中間件和錯誤中間件的?

// regular middleware
app.use(function (req, res, next) {
  next(new Error('boom!'));
});

// error middleware
app.use(function onerror(err, req, res, next) {
  // an error occurred!
});

JavaScript的函數的長度屬性:length。微信

這麼說可能比較繞,看下面這個例子就懂了。前端工程師

例如數據結構

function test1(foo,bar){ }
test.length // 2

function test2(foo,bar,baz){ }
test.length // 3

connect正是經過中間件處理函數的形參長度來區分出普通中間件和錯誤中間件的。app

function call(handle, route, err, req, res, next) {
  var arity = handle.length;
  var error = err;
  var hasError = Boolean(err);

  try {
    if (hasError && arity === 4) {
      // error-handling middleware
      handle(err, req, res, next);
      return;
    } else if (!hasError && arity < 4) {
      // request-handling middleware
      handle(req, res, next);
      return;
    }
  } catch (e) {
    // replace the error
    error = e;
  }

  // continue
  next(error);
}

看了源碼,官方文檔對錯誤處理中間件描述skipping any error middleware above that middleware and any non-error middleware below的解釋其實也懂了:

  • 跳過前面的全部錯誤中間件:index值是遞增的,請求只走後面的錯誤中間件
  • 跳事後面的非異常處理中間件:異常中間件兩個條件都知足請求會進入,非異常中間件因爲hasError爲true所以請求不會進入

只能有一個異常處理中間件嗎?
能夠有多個。(官方文檔+親測)

app.use(logErrors)
app.use(clientErrorHandler)
app.use(errorHandler)
// error middleware one
app.use(function onerror(err, req, res, next) {
  // an error occurred!
  next(err) // 注意要這麼寫,next()進入不到下一個異常處理中間件
});
// error middleware two
app.use(function onerror(err, req, res, next) {
  // an error occurred!
});

中間件處理函數中的next指代的又是什麼?

指代的是棧中的下一個中間件。

proto.handle = function handle(req, res, out) {
  var index = 0;
  var stack = this.stack;
  // ...
  function next(err) {
    // next callback
    var layer = stack[index++];

    // call the layer handle
    call(layer.handle, route, err, req, res, next);
  }

  next();
};

經過上面代碼能夠看出,每次調用next()函數,會執行index++,layer爲middleware stack中的下一個中間件。

其中layer.handle來自於this.stack.push({ route: path, handle: handle });

期待和你們交流,共同進步,歡迎你們加入我建立的與前端開發密切相關的技術討論小組:

努力成爲優秀前端工程師!
相關文章
相關標籤/搜索