[英]NodeJS best practices for catching errors
我开始使用NodeJS和Express。 来自其他流行的脚本语言和C ++背景,异步调用DB函数有点陌生。 我已经整理出一个模式,但是我仍然对捕获异常感到好奇。 下面是我的基本模式。
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');
};
通常,我有很多端点和回调。 我对于在哪里设置ta try / catch块来捕获回调中引发的错误有些迷惑。 我到处寻找一些建议,但是很多建议似乎都在使用Web框架(如果有)上。
当您引发异步回调时,该异常仅返回到数据库事件处理程序的内部,并且您无法捕获或处理该异常。 因此,基本上根本没有好处。 这只会导致您中止对该请求的处理,并且永远不会发送对该请求的响应。
基本上,您有几种选择方式来处理错误。 您可以在每个端点中完全正确地处理它,并发送某种错误响应。
在每个错误点发送响应
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)
});
有关在Express中具有通用错误处理程序的方式的更多信息,请参见Nodejs处理不支持的URL和请求类型 。
使用承诺收集每个路线中的错误
如果您正在使用更多的异步操作序列,其中可能有多个异步操作一起排序,那么在每个异步操作中处理错误确实很痛苦。 这是在所有异步操作中更轻松地使用.catch()
,所有错误都可以渗透到每个路由的顶层的一个.catch()
语句中。 您没有说您正在使用什么数据库,但这是一个看起来像什么的想法。 一般想法是,您可以编写代码,以便所有承诺拒绝(例如错误)都将传播到每个路由处理程序中的一个中央.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内置了对Promise的支持,而ES7将增加对异步操作的异步/等待的支持(基于Promise),并且所有提供异步操作的重要库都已添加或正在添加对Promise的支持,因此,很显然,promise是用于管理异步操作的语言的未来。 那将是我的强烈建议。
您永远都不会抛出这样的错误! :)原因是由于某些数据库查询失败,有时您的整个节点应用程序将停止运行。 这应该得到处理,而不仅仅是死掉。
并且由于这是一个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的实际签名是next(err)
。 因此,如果您不带任何参数调用它,它将仅跳至下一个中间件。 如果使用参数调用它,它将跳过所有常规函数并转到堆栈中的最后一个函数,或更具体地说,将其处理错误。 它们就像常规参数一样, 但是采用四个参数,而不是三个:
app.use(function (err, req, res, next) { });
理解如果您接下来使用参数调用该函数将非常重要 。 抛出错误不会有任何好处! 当然,如果您的路由均不符合特定条件(url),则将调用呼叫中的最后一个条件,因此您仍然可以处理“未找到”错误。
这是您将使用的常见方案:
// 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: {}
});
});
希望有帮助! :)
由于您使用的是express,因此它具有自己的异常处理方式,如下所示:
function clientErrorHandler (err, req, res, next) {
if (req.xhr) {
res.status(500).send({ error: 'Something failed!' })
} else {
next(err)
}
}
app.use(clientErrorHandler)
有关更多信息:
我们通常需要考虑三种最主要的错误类型。
如强循环文档或node js 2018最佳实践中所建议,为了处理承诺失败,重要的是要有一个通用的函数来处理它。
// app.js file
app.get('/:id', async (req,res,next) => {
if(!req.params.id) {
return res.status(412).send('enter a valid user id');
}
try {
const results = await UserDAL(id);
} catch(e) {
next(e);
}
}
// common error middleware defined in middleware/error.js
module.exports = function (err,req,res,next) {
logger.error(`${err.status || 500} - ${err.message} - ${req.originalUrl} - ${req.method} - ${req.ip}`);
return res.status(500).send('something failed.');
};
未处理的拒绝
process.on('unhandledRejection',e => {//做某事});
未处理的异常
process.on('uncaughtException',e => {//做某事});
如果您在express方法中看到很多try / catch块,则可以将其抽象为一个单独的异步函数,如下所示:
module.exports = function asyncMiddleWare(handler) {
return async (req,res,next) => {
try {
await handler(req,res)
} catch(e) {
next(e);
}
}
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.