我在某些网站上读到了express.js中的“异步代码中任何未捕获的错误都可能导致HTTP服务器崩溃而导致DoS”。 我已经通过本示例进行了检查,但是我想知道为什么如果在Express回调内发生错误,服务器不会崩溃,但是如果在setTimeout()函数内部发生,则服务器崩溃。
两个示例中的错误均不是在异步代码中发生的吗?还是其中之一不是异步的而我弄错了? 为什么某些异步代码中未捕获的错误会使服务器崩溃,而其他异步代码中却没有崩溃?
var express = require("express");
var app = express();
http: app.get("/e1", (req, res, next) => {
let p = req.query.p;
let pn = parseInt(p, 10);
//If the error happens here the server does not crashes
let s = pn + y; // y does not exist, so an error occurs
res.send("hi");
});
http: app.get("/e2", (req, res, next) => {
let p = req.query.p;
let pn = parseInt(p, 10);
setTimeout(() => {
//If the error happens here the server crashes
let s = pn + y; // y does not exist, so an error occurs
}, 100);
res.send("hi");
});
app.listen(3000, function() {
console.log("Example app listening on port 3000!");
});
答案 0 :(得分:0)
如果我们想到throw
和catch
在堆栈上运行,可能会变得很清楚:
throw
:在堆栈中向下移动,直到找到处理程序,然后从那里继续。
catch
将错误处理程序添加到堆栈中。
对于同步代码,可以将其可视化为:
// Legend:
-> function call
<- function returns
http.request -> express.handler -> [try] -> your function -> nested call -> Throw!
<- <- [catch] <-----------------------------------
现在,当您启动异步操作时,某个回调将在某个时候被回调,并且该回调将最终在新的堆栈上:
// the current stack:
http.request -> express.handler -> [try] -> your function -> start async action
<- <- <- <- <-
// somewhen later, the timeout calls back
timer -> your setTimeout callback -> nested call -> Throw!
Crash! <-----------------------------------------------------
现在,由于Express将catch
处理程序附加到回调中的代码中:
Express.get = function(callback) {
//...
try {
callback(req, res, next);
} catch(error) {
// handle error
}
};
将处理错误,但仅处理同步错误(简化后,实际代码为here)
现在该怎么办?
基本上:将每个回调都包装到一个promise中(这样可以简化异步错误处理):
const delay = ms => new Promise(res => setTimeout(res, ms));
然后await
创建的每个承诺,然后将所有内容包装在try
/ catch
中:
app.get(async (req, res) => {
try {
await delay(2000);
const p = q + d;
} catch(error) {
res.status(500).send("whoops");
}
});
之所以可行,是因为await
会“保持堆栈”(但只是async
的一个函数在嵌套调用中await
,这就是我们需要添加自己的{{1} } / try
,因为Express不会在回调调用上catch
),因此嵌套await
ed函数之一内部的错误将退回到我们的错误处理程序。
await