我开始使用NodeJS和Express。来自其他流行的脚本语言和C ++背景,异步调用数据库函数有点陌生。我已经整理出一个模式,但我仍然对捕获异常感到好奇。以下是我的基本模式。
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块的地方有点迷失,以捕获回调中抛出的错误。我四处寻找一些建议,但其中很多似乎都在使用网页框架(如果有的话)。
答案 0 :(得分:8)
当您引入异步回调时,异常只会返回到数据库事件处理程序的内部,并且您无法捕获或处理该异常。所以,基本上它根本没有好处。它只会导致您中止对该请求的处理,并且您永远不会对该请求发送响应。
基本上,您有多种选择来处理错误。您可以在每个端点完全处理它并发送某种错误响应。
在每个错误点发送响应
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 handle unsupported URLs and request types。
使用promises收集每条路线中的错误
如果您正在使用更多涉及的异步操作序列,其中您可能有多个异步操作一起排序,那么在每个异步操作中处理错误确实很麻烦。这是使用所有异步操作的promises更容易允许所有错误在每个路由的顶层渗透最多一个.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内置支持promises,ES7将为异步操作(基于promises)添加异步/等待支持,并且所有提供异步操作的重要库都添加或正在添加对promises的支持,很明显promises是管理异步操作的语言的未来。那是我的强烈建议。
答案 1 :(得分:3)
你永远不应该抛出这样的错误! :)原因是在某些时候你的整个节点应用程序将停止工作,因为一些数据库查询失败。这应该被处理,而不是只是死。
因为这是一个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: {}
});
});
希望有所帮助! :)
答案 2 :(得分:1)
由于您使用express,它有自己的方式来处理异常, 定义如下:
function clientErrorHandler (err, req, res, next) {
if (req.xhr) {
res.status(500).send({ error: 'Something failed!' })
} else {
next(err)
}
}
app.use(clientErrorHandler)
欲了解更多信息:
答案 3 :(得分:0)
我们通常需要考虑三种最主要的错误类型。
如强循环文档或node js 2018 best practices中所述,为了处理承诺失败,重要的是要有一个通用的函数来处理它。
// 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);
}
}
};