2017-03-05 103 views
1

我剛開始有NodeJS和Express。從其他流行的腳本語言和C++背景來看,異步調用DB函數有點異類。我已經整理出一個模式,但我仍然對捕捉異常很好奇。以下是我的基本模式。NodeJS捕捉錯誤的最佳實踐

var callback = function(req, res) { 
    // do stuff 
    connection.query(queryString, function(err,result){ 
     if (err) throw err; 
     // process results. 
    }; 
}; 

var express = require('express'); 
var app  = express(); 

app.get('/', callback); 
app.listen(3000,function() { 
    console.log('listening'); 
}; 

通常我有很多終結點和回調。我有點失落,我設置了try/catch塊來捕獲回調中拋出的錯誤。我已經四處尋找一些建議,但它們中的很多似乎都在使用Web框架(如果有的話)。

+0

我發現迄今爲止在Express和Node中異步錯誤處理的最佳資源是StrongLoop的這篇博文https://strongloop.com/strongblog/async-error-handling-expressjs-es7-promises-generators/團隊落後於Express。 – korun

回答

5

當您在異步回調拋出,異常只是去回數據庫事件處理程序的內部,也沒有辦法讓你能趕上或處理該異常。所以,基本上它根本就沒有好處。它只會導致您放棄對該請求的處理,您將永遠不會針對該請求發送響應。

基本上,你有幾個選擇如何處理錯誤。您可以在每個端點完全處理它併發送某種錯誤響應。就在錯誤的每個點

app.get('/', function(req, res) { 
    // do stuff 
    connection.query(queryString, function(err,result){ 
     if (err) return res.status(500).send(someErrorResponse); 
     // process results. 
    }; 
}); 

轉發到集中的錯誤處理程序

或者,您也可以通過調用next(err)轉發到一個集中的錯誤處理程序中的錯誤

發送響應:

app.get('/', function(req, res, next) { 
    // do stuff 
    connection.query(queryString, function(err,result){ 
     // if error, forward it on to our centralized error handler 
     if (err) return next(err); 
     // process results. 
    }; 
}); 

// centralized error handler - note how it has four parameters 
app.use(function(err, req, res, next) { 
    // formulate an error response here 
    console.log(err); 
    res.status(500).send(someErrorMessage) 
}); 

請參閱Nodejs handle unsupported URLs and request types以獲取有關在Express中使用通用錯誤處理程序的更多信息。

使用承諾每條路線中收集的錯誤

如果您正在使用,你可以有一個以上的異步操作測序在一起異步操作的更復雜的序列,那麼它得到的是一個痛苦的處理每個異步操作中的錯誤。這是在所有異步操作中更容易使用promise的地方,允許所有錯誤在每個路由的最高級別滲透多達一條.catch()聲明。你不會說你正在使用什麼數據庫,但是這裏有一個想法。總的想法是,你可以寫你的代碼,使所有承諾拒絕(例如錯誤)將在每個路由處理程序傳播到一箇中央.catch(),然後你可以調用next(err).catch(),發送錯誤的集中錯誤處理程序。下面是如何使用一個數據庫操作尋找最近版本的Mongoose(你沒有說你使用哪個數據庫)。

app.get('/', function(req, res, next) { 
    // do stuff 
    connection.query(queryString).exec().then(function(result){ 
     // process results. 
    }).catch(next); 
}); 

// centralized error handler - note how it has four parameters 
app.use(function(err, req, res, next) { 
    // formulate an error response here 
    console.log(err); 
    res.status(500).send(someErrorMessage) 
}); 

而且,這裏是什麼樣子,如果你有一個以上的操作:

app.get('/', function(req, res, next) { 
    // do stuff 
    connection.query(queryString).exec().then(function(result){ 
     // process results, then make another query 
     // return the promise from this second operaton so both results 
     // and error are chained to the first promise 
     return connection.query(...).exec(); 
    }).then(function(result) { 
     // process chained result 
    }).catch(next); 
}); 

// centralized error handler - note how it has four parameters 
app.use(function(err, req, res, next) { 
    // formulate an error response here 
    console.log(err); 
    res.status(500).send(someErrorMessage) 
}); 

由於ES6內置支持的承諾和ES7將增加對異步支持/等待異步操作(這是基於承諾的),並且所有提供異步操作的重要庫都添加了或正在增加對promise的支持,很明顯承諾是用於管理異步操作的語言的未來。這將是我強烈的建議。

+0

令人驚歎。非常感謝! –

1

由於您使用快遞,它有它自己的方式來處理異常, 這樣定義:

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

欲瞭解更多信息:

https://expressjs.com/en/guide/error-handling.html

2

你應該永遠不會丟這樣的錯誤! :)原因是,在某些時候,您的整個節點應用程序將停止工作,因爲某些數據庫查詢失敗。這應該被處理,而不是隻是死。

而且因爲這是一個route處理程序 - 處理用戶正在獲取的特定url(例如/),應該有一些輸出。如果出現這樣一個你無法處理的錯誤,或者你的內部狀態可能混亂,你總是可以顯示狀態爲500的頁面和一個不錯的設計。

所以基本上只是沒有任何事情發生 - 返回任何一種respones,甚至render頁面,但提供的信息出了問題。

另外,一個常見的場景就像Alon Oz提供的一樣。所有express的路由實際上都是一箇中間件函數,它們被一個接一個地調用。如果路線與請求路線不匹配,則該功能會跳過並調用下一個路線。你可以手動控制。路由器的實際模式是這樣的:

app.get('/', function(req, res, next) { 
    // you can have the request 
    // you can send response like res.send('hello') 
    // OR you can skip this function using NEXT 
}); 

接下來的實際簽名是next(err)。所以,如果你沒有任何爭論的話,它只會跳到下一個中​​間件。如果使用參數調用它,它將跳過所有常規函數並轉到堆棧中的最後一個函數,或者更具體地說,處理錯誤的函數。他們就像普通的人,但採取四個參數,而不是三個:

app.use(function (err, req, res, next) { }); 

明白,這個函數將被調用,如果你電話下一個有爭論這是非常重要的。拋出一個錯誤將無濟於事!當然,如果你的路線都不符合特定的標準(url),那麼最後一個路線將被調用,所以你仍然可以處理「not found」錯誤。

這是您將使用一個常見的場景:

// development error handler, will print stacktrace 
if (app.get('env') === 'development') { 
    app.use(function(err, req, res, next) { 
     debug('ERROR [ip: %s]:: dev env -> ', req.ip, err); // I'm using debug library - very helpful 
     res.status(err.status || 500); 
     res.render('deverr', { // I render custom template with the whole stack beautifully displayed 
      errMessage: err.message, 
      error: err 
     }); 
    }); 
} 

// production error handler, no stacktraces leaked to user 
app.use(function(err, req, res, next) { 
    res.status(err.status || 500); 
    res.render('pages/error', { // custom error page with nice design and a message 
     errMessage: err.message, 
     error: {} 
    }); 
}); 

希望幫助!:)

+0

我覺得在開發環境/單元測試之外像這樣投擲會很糟糕。因此,這個問題。非常感謝您的深入解答。但是,幫助了很多。這是一個咀嚼,但乍一看,這是非常有道理的。 –