2017-03-05 79 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

我觉得在开发环境/单元测试之外像这样投掷会很糟糕。因此,这个问题。非常感谢您的深入解答。但是,帮助了很多。这是一个咀嚼,但乍一看,这是非常有道理的。 –