简体   繁体   English

Node.js-在发生意外错误时提供错误500页

[英]Node.js - Serving error 500 pages on unexpected errors

I'm trying to serve 500 pages (some generic HTML that says "500 - internal server error") from my Node.js server to requests that failed to resolve due to developer bugs, but can't find an elegant way to do this. 我试图从我的Node.js服务器提供500页(某些通用HTML内容为“ 500-内部服务器错误”)的请求,以解决由于开发人员错误而无法解决的请求,但找不到一种优雅的方法。

Lets say we have the following index.js, where a developer innocently made a mistake: 可以说我们有以下index.js,其中开发人员无辜地犯了一个错误:

const http = require('http');
const port = 12345;

http.createServer(onHttpRequest).listen(port);

function onHttpRequest(req, res) {
    var a = null;
    var b = a.c; // this is the mistake

    res.end('status: 200');
}

Trying to access property "c" of null throws an error, so "res.end" will never be reached. 尝试访问null的属性“ c”会引发错误,因此将永远无法访问“ res.end”。 As a result, the requesting client will eventually get a timeout. 结果,发出请求的客户端最终将超时。 Ideally, I my server to have code that can catch errors like this, and return 500 pages to the requesting client (as well as email an administrator and so on). 理想情况下,我的服务器应具有可以捕获此类错误的代码,并向请求的客户端返回500页(以及向管理员发送电子邮件等)。

Using "try catch" in every single block is out of the question. 在每个块中都使用“ try catch”是不可能的。 Most Node.js code is async, and a lot of the code relies on external libraries with questionable error handling. 大多数Node.js代码都是异步的,并且很多代码都依赖于外部库,这些库具有错误的错误处理。 Even if I use try-catch everywhere, there's a chance that an error would happen in an external library that didn't have a try-catch block inside of it, in a function that happens asynchronously, and thus my server will crash and the client would never get a response. 即使我在所有地方都使用try-catch,在异步执行的函数中,内部没有try-catch块的外部库中也可能会发生错误,因此我的服务器将崩溃,并且客户永远都不会得到回应。

Shortest example I can provide: 我可以提供的最短示例:

/* my server's index.js */

const http = require('http');
const poorlyTestedNpmModule = require('some-npm-module');
const port = 12345;

http.createServer(onHttpRequest).listen(port);

function onHttpRequest(req, res) {
    try {
        poorlyTestedNpmModule(null, onResult);
    }
    catch(err) {
        res.end('status: 500');
    }

    function onResult(err, expectedResult) {
        if(err) {
            res.end('status: 400');
        }
        else {
            res.end('status: 200');
        }
    }
}

/* some-npm-module.js */

module.exports = function poorlyTestedNpmModule(options, callback) {
    setTimeout(afterSomething, 100);

    function afterSomething() {
        var someValue = options.key; // here's the problem
        callback(null, someValue);
    }
}

Here, the server crashes, due to a function call that led to code that asynchronously throws an error. 在这里,由于函数调用导致代码异步抛出错误,服务器崩溃。 This code is not code that I control or wish to modify; 此代码不是我控制或希望修改的代码; I want my server to be able to handle all those errors on its own. 我希望我的服务器能够自行处理所有这些错误。

Now, I could, for instance, just use the global uncaughtException event, ie: process.on('uncaughtException', doSomething); 现在,例如,我可以只使用全局uncaughtException事件,即:process.on('uncaughtException',doSomething);

but then I have no access to the (req, res) arguments, making it impossible to call res.end for the correct res instance; 但是后来我无法访问(req,res)参数,因此无法为正确的res实例调用res.end; the only way to have access to them, is to store them in a higher-scope object for each incoming request, and then prune them on successful request resolutions, then mark existing [req, res] stored pairs as "potentially errored" whenever an uncaughtException triggers, and serve 500 pages to those requests whenever the count of currently-active requests matches the count of currently-unresolved-errors (and re-test that count per thrown uncaught expection and per successful res.end call). 对其进行访问的唯一方法是,针对每个传入请求将它们存储在更高范围的对象中,然后根据成功的请求解决方案对其进行删节,然后在存在以下问题时将现有的[req,res]存储对标记为“潜在错误” uncaughtException触发器触发,并在当前活动请求的数量与当前未解决的错误的数量匹配时为这些请求提供500页(并重新测试每个未抛出的期望和成功的res.end调用的计数)。

Doing that works, but... it's ugly as hell. 这样做是可行的,但是……真是丑陋。 It means that request objects have to be leaked to the global scope, and it also means that my router module now has a dependency on the uncaughtException global event, and if any other code overwrites that event, everything breaks, or if I ever want to handle other uncaught exceptions for whatever reason, I'll run into cross dependency hell. 这意味着必须将请求对象泄漏到全局范围,这也意味着我的路由器模块现在对uncaughtException全局事件具有依赖性,并且如果任何其他代码覆盖了该事件,则一切都将中断,或者如果我想无论出于何种原因处理其他未捕获的异常,我都会遇到交叉依赖地狱。

The root cause of this problem is that an unexpected error can happen anywhere, but I want to specifically catch whether an unexpected error originated from a stack trace that began from an incoming http request (and not, for example, from some interval I have running in the background, because then I get an unexpected error but obviously don't want to serve a 500 page to anyone, only email an admin with an error log), and on top of needing to know whether the error originated from an http request, I need to have access to the request+response objects that node server objects provide. 造成此问题的根本原因是,任何地方都可能发生意外错误,但是我想专门捕获意外错误是否源于从传入的HTTP请求开始的堆栈跟踪(例如,不是从我运行的某个时间间隔开始)在后台,因为这样我会收到一个意外错误,但显然不想为任何人提供500页的服务,仅向管理员发送错误日志给电子邮件),并且还需要了解错误是否源自http请求,我需要访问节点服务器对象提供的request + response对象。

Is there no better way? 有没有更好的办法?

[Edit] The topic of this question is role distribution in modules. [编辑]这个问题的主题是模块中的角色分配。

ie, one guy is making base code for a server, lets say a "router module". 也就是说,有一个人正在为服务器创建基础代码,可以说是“路由器模块”。 Other people will add new code to the server in the future, handling branches that are routed to. 将来,其他人将向服务器添加新代码,以处理路由到的分支。

The guy that writes the base server code has to write it in a way that it will serve 500 pages if any future code is written incorrectly and throws errors. 编写基本服务器代码的人必须以某种方式编写它,如果将来任何不正确的代码编写并引发错误,它将服务500页。 Help him accomplish his goal. 帮助他实现目标。

Answers of the format "make sure all future people that add code never make mistakes and always write code that won't throw uncaught errors" will not be accepted. 格式为“确保所有将来添加代码的人都不会犯错误并且始终编写不会抛出未捕获的错误的代码”的答案将不被接受。

At first, using uncaughtException in Nodejs is not safe. 首先,在Node.js中使用uncaughtException是不安全的。 If you feel that there is no other option in your application, make sure that you exit the process in the handler of 'uncaughtException' and restart the process using pm2 or forever or someother modules. 如果您觉得应用程序中没有其他选项,请确保在“ uncaughtException”处理程序中退出进程,然后使用pm2或永久或其他模块重新启动该进程。 Below link can provide you its reference. 下面的链接可以为您提供参考。

Catch all uncaughtException for Node js app 捕获Node js应用程序的所有uncaughtException

Coming to the process of error handling, as mentioned, you may always miss to handle errors with callback. 如前所述,在错误处理过程中,您可能总是会错过通过回调处理错误的机会。 To avoid, these we can use an exceptional advantage of promises in nodejs. 为避免这种情况,我们可以在nodejs中使用promises的特殊优势。

/* my server's index.js */

const http = require('http');
const poorlyTestedNpmModule = require('some-npm-module');
const port = 12345;

http.createServer(onHttpRequest).listen(port);

function onHttpRequest(req, res) { 

   try {
            poorlyTestedNpmModule(null)
            .then(result => {
                res.end('status: 200');
            })
            .catch(err =>{
              console.log('err is', err);
              res.end('status: 400');
            })
       }
    catch(err) {
                res.end('status: 500');
    }

}


/* some-npm-module.js */

module.exports = function poorlyTestedNpmModule(options, callback) {
    setTimeout(afterSomething, 100);

   afterSomthing = new Promise((resolve, reject)=> {
       var someValue = options.key; // here's the problem
       resolve(someValue);

   })
}

If you see that some of the npm nodemodules are not present with promise, try to write wrappers to convert callback to promise model and use them in your application. 如果您看到某些npm节点模块不带有promise,请尝试编写包装程序以将回调转换为promise模型,然后在应用程序中使用它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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