如何处理node.js中的代码异常?

lstz6jyr  于 2023-03-01  发布在  Node.js
关注(0)|答案(5)|浏览(519)

我浏览了Express的文档,the part describing error handling对我来说是完全不透明的。
我想他们所指的app是一个示例createServer(),对吗?但是我不知道如何阻止node.js在处理请求过程中发生异常时破坏应用程序进程。
我不需要什么花哨的东西只要有异常,我就想返回一个状态500,加上一个空响应,节点进程不能仅仅因为某个地方有一个未捕获的异常而终止。
有没有一个简单的例子来说明如何实现这一点?

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

var app = express.createServer();

app.get('/', function(req, res){
    console.log("debug", "calling")
    var options = {
        host: 'www.google.com',
        port: 80,
        path: "/"
    };
    http.get(options, function(response) {
       response.on("data", function(chunk) {
           console.log("data: " + chunk);
           chunk.call(); // no such method; throws here
       });
    }).on('error', function(e) {
       console.log("error connecting" + e.message);
    });
});

app.configure(function(){
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.listen(3000);

整个应用程序崩溃,产生回溯

mypath/tst.js:16
           chunk.call(); // no such method; throws here
                 ^ TypeError: Object ... has no method 'call'
    at IncomingMessage.<anonymous> (/Library/WebServer/Documents/discovery/tst.js:16:18)
    at IncomingMessage.emit (events.js:67:17)
    at HTTPParser.onBody (http.js:115:23)
    at Socket.ondata (http.js:1150:24)
    at TCP.onread (net.js:374:27)
daolsyd0

daolsyd01#

如果您真的想捕获所有异常并提供一些处理而不是退出Node.js进程,那么您需要处理Node的uncaughtException事件。
如果你仔细想想,这是一个Node的东西,而不是Express的东西,因为如果你从某段任意代码中抛出一个异常,不能保证Express能够或将永远看到它,或者是在一个位置来捕获它。(为什么?异常与节点风格的异步事件驱动回调代码交互不太好。异常沿着调用堆栈向上移动,找到抛出异常时在作用域中的catch()块。如果myFunction将一些工作推迟到某个事件发生时运行的回调函数,然后返回事件循环,那么当回调函数被调用时,它直接从主事件循环中调用,并且myFunction不再位于调用堆栈中;如果这个回调函数抛出了一个异常,那么即使myFunction有一个try/catch块,它也不会捕获这个异常。)
实际上,这意味着如果你抛出一个异常,而不是自己捕获它,而是在Express直接调用的函数中捕获,那么Express可以捕获这个异常并调用你安装的错误处理程序,前提是你已经配置了一些错误处理中间件,比如app.use(express.errorHandler())。但是如果你在一个响应异步事件的函数中抛出同一个异常,Express将无法捕获它。(它能够捕获它的唯一方法是侦听全局Node uncaughtException事件,这将是一个坏主意,首先因为它是全局的,您可能需要将它用于其他事情,其次因为Express将不知道什么请求与该异常相关联。)
下面是一个例子,我将这段路线处理代码添加到现有的Express应用程序中:

app.get('/fail/sync', function(req, res) {
   throw new Error('whoops');
});
app.get('/fail/async', function(req, res) {
   process.nextTick(function() {
      throw new Error('whoops');
   });
});

现在,如果我在浏览器中访问http://localhost:3000/fail/sync,浏览器会转储调用堆栈(显示express.errorHandler的运行)。但是,如果我在浏览器中访问http://localhost:3000/fail/async,浏览器会生气(Chrome显示“No data received:错误324,网络::错误空响应:服务器关闭了连接,没有发送任何数据”消息),因为Node进程已经退出,在我调用它的终端中显示了对stdout的回溯。

wztqucjr

wztqucjr2#

为了能够捕获异步错误,我使用了域。使用Express,您可以尝试以下代码:

function domainWrapper() {
    return function (req, res, next) {
        var reqDomain = domain.create();
        reqDomain.add(req);
        reqDomain.add(res);

        res.on('close', function () {
            reqDomain.dispose();
        });
        reqDomain.on('error', function (err) {
            next(err);            
        });
        reqDomain.run(next)
    }
}
app.use(domainWrapper());
//all your other app.use
app.use(express.errorHandler());

这段代码将捕获异步错误并发送到错误处理程序。在这个例子中,我使用express.errorHandler,但它可以用于任何处理程序。
有关域的详细信息:http://nodejs.org/api/domain.html

gojuced7

gojuced73#

您可以使用express使用的默认错误处理程序,它实际上是connect error handler

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

app.get('/', function(req, res){
  throw new Error('Error thrown here!');
});

app.configure(function(){
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.listen(3000);

更新对于您的代码,您实际上需要捕获错误并将其传递给express,如下所示

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

var app = express.createServer();

app.get('/', function (req, res, next) {
  console.log("debug", "calling");
  var options = {
    host:'www.google.com',
    port:80,
    path:"/"
  };
  http.get(options,
    function (response) {
      response.on("data", function (chunk) {
        try {
          console.log("data: " + chunk);
          chunk.call(); // no such method; throws here

        }
        catch (err) {
          return next(err);
        }
      });
    }).on('error', function (e) {
      console.log("error connecting" + e.message);
    });
});

app.configure(function () {
  app.use(express.errorHandler({ dumpExceptions:true, showStack:true }));
});

app.listen(3000);
wmvff8tz

wmvff8tz4#

express 5.0.0-alpha.7 came out 27 days ago。有了这个非常具体的预发布版本,您现在终于可以在请求处理程序中拒绝一个承诺,并且它将得到正确的处理:
中间件和处理程序现在可以返回promise,如果promise被拒绝,next(err)将被调用,err是拒绝的值。
例如:

app.get('/', async () => {
    throw new Error();
});
app.use((err, req, res, next) => {
    res.status(500).send('unexpected error :(');
});

但是,只能将其作为一种后备措施,正确的错误处理仍然应该在请求处理程序本身的标语中进行,并带有正确的错误状态代码。

ddhy6vgd

ddhy6vgd5#

如果你没有在你的应用程序中捕捉到一个未被捕捉到的异常,它将灾难性地崩溃,这意味着服务器进程将以非零错误代码退出,用户将永远看不到任何响应。如果你没有安装像pm2这样的进程管理器,它也将保持死状态。为了避免这种情况,并捕捉每一种可能的错误,如逻辑错误或程序员错误,你需要把代码放在一个try-catch块中。然而,有一个非常简单的解决方案,可以避免在每个控制器函数周围都有一个try-catch块。这个article解释了如何做到这一点。
只需安装express-async-errors并将其置于您的app.js之上:

const express = require('express');
require('express-async-errors');
...

现在控制器中的每个可能的错误都会自动传递给express error handler,无论是异步还是同步,即使你写了null.test这样的东西,你也可以像下面这样定义一个错误处理程序:

app.use((err, req, res, next) => {
    console.error(err.stack)
    res.status(500).send('Something broke!')
});

为了让它更有用,你甚至可以定义你自己的错误类,这些类继承自Errorthrow,在你的控制器中的任何地方都可以,它会被Express自动捕获。然后在你的错误处理程序中选中if (err instanceof MyCustomErrorClass),在你的500页面中显示自定义消息。

相关问题