Express 文檔(錯誤處理)

錯誤處理

錯誤處理是指Express如何捕獲和處理同步和異步發生的錯誤,Express附帶一個默認的錯誤處理程序,所以你無需編寫本身的錯誤處理程序便可開始使用。json

捕捉錯誤

確保Express捕獲運行路由處理程序和中間件時發生的全部錯誤很是重要。segmentfault

路由處理程序和中間件內的同步代碼中發生的錯誤不須要額外的工做,若是同步代碼拋出錯誤,則Express將捕獲並處理它,例如:promise

app.get("/", function (req, res) {
  throw new Error("BROKEN"); // Express will catch this on its own.
});

對於由路由處理程序和中間件調用的異步函數返回的錯誤,必須將它們傳遞給next()函數,Express將捕獲並處理它們,例如:app

app.get("/", function (req, res, next) {
  fs.readFile("/file-does-not-exist", function (err, data) {
    if (err) {
      next(err); // Pass errors to Express.
    }
    else {
      res.send(data);
    }
  });
});

若是將任何內容傳遞給next()函數(字符串'route'除外),則Express將當前請求視爲錯誤,並將跳過任何剩餘的非錯誤處理路由和中間件函數。框架

若是序列中的回調不提供數據,只提供錯誤,則能夠按以下方式簡化此代碼:異步

app.get("/", [
  function (req, res, next) {
    fs.writeFile("/inaccessible-path", "data", next);
  },
  function (req, res) {
    res.send("OK");
  }
]);

在上面的示例中,next做爲fs.writeFile的回調提供,調用時有或沒有錯誤,若是沒有錯誤,則執行第二個處理程序,不然Express會捕獲並處理錯誤。ide

你必須捕獲由路由處理程序或中間件調用的異步代碼中發生的錯誤,並將它們傳遞給Express進行處理,例如:函數

app.get("/", function (req, res, next) {

  setTimeout(function () {
    try {
      throw new Error("BROKEN");
    }
    catch (err) {
      next(err);
    }
  }, 100);
});

上面的示例使用try...catch塊來捕獲異步代碼中的錯誤並將它們傳遞給Express,若是省略try...catch塊,Express將不會捕獲錯誤,由於它不是同步處理程序代碼的一部分。ui

使用promises能夠避免try...catch塊的開銷或者使用返回promises的函數,例如:this

app.get("/", function (req, res, next) {
  Promise.resolve().then(function () {
    throw new Error("BROKEN");
  }).catch(next); // Errors will be passed to Express.
});

因爲promises會自動捕獲同步錯誤和拒絕promises,你能夠簡單地提供next做爲最終的catch處理程序,Express將捕獲錯誤,由於catch處理程序被賦予錯誤做爲第一個參數。

你還可使用處理程序鏈來依賴同步錯誤捕獲,經過將異步代碼減小爲一些簡單的代碼,例如:

app.get("/", [
  function (req, res, next) {
    fs.readFile("/maybe-valid-file", "utf8", function (err, data) {
        res.locals.data = data;
        next(err);
    });
  },
  function (req, res) {
    res.locals.data = res.locals.data.split(",")[1];
    res.send(res.locals.data);
  }
]);

上面的例子有一些來自readFile調用的簡單語句,若是readFile致使錯誤,那麼它將錯誤傳遞給Express,不然你將快速返回到鏈中下一個處理程序中的同步錯誤處理的世界。而後,上面的示例嘗試處理數據,若是失敗,則同步錯誤處理程序將捕獲它,若是你在readFile回調中完成了此處理,則應用程序可能會退出,而且Express錯誤處理程序將沒法運行。

不管使用哪一種方法,若是要調用Express錯誤處理程序並使應用程序存活,你必須確保Express收到錯誤。

默認錯誤處理程序

Express附帶了一個內置的錯誤處理程序,能夠處理應用程序中可能遇到的任何錯誤,此默認錯誤處理中間件函數添加在中間件函數堆棧的末尾。

若是你將錯誤傳遞給next()而且你沒有在自定義錯誤處理程序中處理它,它將由內置錯誤處理程序處理,錯誤將堆棧跟蹤寫入客戶端,堆棧跟蹤不包含在生產環境中。

將環境變量 NODE_ENV設置爲 production,以在生產模式下運行應用程序。

若是在開始寫入響應後調用next()並出現錯誤(例如,若是在將響應流式傳輸到客戶端時遇到錯誤),則Express默認錯誤處理程序將關閉鏈接並使請求失敗。

所以,當你添加自定義錯誤處理程序時,必須在headers已發送到客戶端時委託給默認的Express錯誤處理程序:

function errorHandler (err, req, res, next) {
  if (res.headersSent) {
    return next(err)
  }
  res.status(500)
  res.render('error', { error: err })
}

請注意,若是你在你的代碼調用next()出現錯誤屢次,則會觸發默認錯誤處理程序,即便自定義錯誤處理中間件已就緒也是如此。

編寫錯誤處理程序

以與其餘中間件函數相同的方式定義錯誤處理中間件函數,除了錯誤處理函數有四個參數而不是三個:(err, req, res, next),例如:

app.use(function (err, req, res, next) {
  console.error(err.stack)
  res.status(500).send('Something broke!')
})

你能夠在其餘app.use()和路由調用以後定義錯誤處理中間件,例如:

var bodyParser = require('body-parser')
var methodOverride = require('method-override')

app.use(bodyParser.urlencoded({
  extended: true
}))
app.use(bodyParser.json())
app.use(methodOverride())
app.use(function (err, req, res, next) {
  // logic
})

中間件函數內的響應能夠是任何格式,例如HTML錯誤頁面、簡單消息或JSON字符串。

對於組織(和更高級別的框架)目的,你能夠定義多個錯誤處理中間件函數,就像使用常規中間件函數同樣,例如,爲使用XHR和不使用XHR的請求定義錯誤處理程序:

var bodyParser = require('body-parser')
var methodOverride = require('method-override')

app.use(bodyParser.urlencoded({
  extended: true
}))
app.use(bodyParser.json())
app.use(methodOverride())
app.use(logErrors)
app.use(clientErrorHandler)
app.use(errorHandler)

在此示例中,通用logErrors可能會將請求和錯誤信息寫入stderr,例如:

function logErrors (err, req, res, next) {
  console.error(err.stack)
  next(err)
}

一樣在此示例中,clientErrorHandler定義以下,在這種狀況下,錯誤會明確傳遞給下一個錯誤。

請注意,在錯誤處理函數中不調用「next」時,你負責編寫(和結束)響應,不然這些請求將「掛起」,而且不符合垃圾回收的條件。

function clientErrorHandler (err, req, res, next) {
  if (req.xhr) {
    res.status(500).send({ error: 'Something failed!' })
  } else {
    next(err)
  }
}

實現「catch-all」的errorHandler函數,以下所示(例如):

function errorHandler (err, req, res, next) {
  res.status(500)
  res.render('error', { error: err })
}

若是你有一個具備多個回調函數的路由處理程序,則可使用route參數跳轉到下一個路由處理程序,例如:

app.get('/a_route_behind_paywall',
  function checkIfPaidSubscriber (req, res, next) {
    if (!req.user.hasPaid) {
      // continue handling this request
      next('route')
    }
    else{
      next();
    }
  }, function getPaidContent (req, res, next) {
    PaidContent.find(function (err, doc) {
      if (err) return next(err)
      res.json(doc)
    })
  })

在此示例中,將跳過getPaidContent處理程序,但app中的/a_route_behind_paywall中的任何剩餘處理程序將繼續執行。

next()next(err)的調用代表當前處理程序已完成並處於什麼狀態, next(err)將跳過鏈中的全部剩餘處理程序,除了那些設置爲處理上述錯誤的處理程序。

上一篇:使用模板引擎

下一篇:調試

相關文章
相關標籤/搜索