繁体   English   中英

NodeJS捕获错误的最佳实践

[英]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)

有关更多信息:

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

我们通常需要考虑三种最主要的错误类型。

  1. 承诺失败(在异步/等待过程中出现的任何失败,或随后/捕获中的承诺结果)

如强循环文档或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.');
};
  1. 未处理的拒绝

    process.on('unhandledRejection',e => {//做某事});

  2. 未处理的异常

    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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM