[英]Node.js Express app handle startup errors
我在 Node.js 和 Express 中有应用程序。 我需要为它编写测试。 我在处理 Express 应用程序错误时遇到问题。 我发现了这个如何捕获像 EADDRINUSE 这样的 node.js/express 服务器错误? ,但它对我不起作用,我不知道为什么。 我想处理在执行 expressApp.listen() 时可能发生的错误(EADDRINUSE、EACCES 等)。
express = require('express')
listener = express()
#doesn't work for me
listener.on('uncaughtException', (err) ->
#do something
)
#doesn't work too
listener.on("error", (err) ->
#do something
)
#this works, but it caughts all errors in process, I want only in listener
process.on('uncaughtException', (err) ->
#do something
)
listener.listen(80) #for example 80 to get error
有任何想法吗?
这应该可以解决问题:
listener.listen(80).on('error', function(err) { });
listener.listen
实际上做的是创建一个 HTTP 服务器并在其上调用 listen:
app.listen = function(){
var server = http.createServer(this);
return server.listen.apply(server, arguments);
};
首先,expressJS 不会抛出uncaughtException
事件,process 会抛出,因此您的代码不起作用也就不足为奇了。
所以使用: process.on('uncaughtException',handler)
代替。
接下来,expressJS 已经提供了一种标准的错误处理方法,即使用它为此提供的中间件功能,如:
app.configure(function(){
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
此函数向客户端返回一条错误消息,带有可选的堆栈跟踪,并记录在connectJS errorHandler中。
(请注意,errorHandler 实际上是 connectJS 的一部分,仅由 expressJS 公开。)
如果现有的 errorHandler 提供的行为不能满足您的需求,其源位于connectJS 的errorHandler
中间件,可以轻松修改以满足您的需求。
当然,与其直接修改这个函数,“正确”的方法是创建你自己的 errorHandler,使用 connectJS 版本作为起点,如下所示:
var myErrorHandler = function(err, req, res, next){
...
// note, using the typical middleware pattern, we'd call next() here, but
// since this handler is a "provider", i.e. it terminates the request, we
// do not.
};
并将其安装到 expressJS 中:
app.configure(function(){
app.use(myErrorHandler);
});
请参阅Just Connect it,已经了解了 connectJS 的filter
和provider
中间件的想法,以及如何为 Connect/Express 编写中间件以获得编写良好的教程。
这些对你也可能有用:
提及: Marius Tibeica的回答是完整而伟大的, david_p评论也是。 Rob Raisch 的回答也是如此(探索很有趣)。 https://stackoverflow.com/a/27040451/7668448
https://stackoverflow.com/a/13326769/7668448
第一种方法很糟糕! 我把它留作参考! 请参阅更新部分! 为了好版本! 以及为什么的解释!
对于那些会觉得这很有用的人,这里有一个函数来实现繁忙的端口处理(如果端口繁忙,它将尝试下一个端口,直到找到一个不繁忙的端口)
app.portNumber = 4000;
function listen(port) {
app.portNumber = port;
app.listen(port, () => {
console.log("server is running on port :" + app.portNumber);
}).on('error', function (err) {
if(err.errno === 'EADDRINUSE') {
console.log(`----- Port ${port} is busy, trying with port ${port + 1} -----`);
listen(port + 1)
} else {
console.log(err);
}
});
}
listen(app.portNumber);
函数 listen 递归调用自身。 如果出现端口繁忙错误。 每次递增端口号。
首先,这个版本是遵循与 nodejs http.Server.listen()
方法相同的签名的版本!
function listen(server) {
const args = Array.from(arguments);
// __________________________________ overriding the callback method (closure to pass port)
const lastArgIndex = arguments.length - 1;
let port = args[1];
if (typeof args[lastArgIndex] === 'function') {
const callback = args[lastArgIndex];
args[lastArgIndex] = function () {
callback(port);
}
}
const serverInstance = server.listen.apply(server, args.slice(1))
.on('error', function (err) {
if(err.errno === 'EADDRINUSE') {
console.log(`----- Port ${port} is busy, trying with port ${port + 1} -----`);
port += 1;
serverInstance.listen.apply(serverInstance, [port].concat(args.slice(2, lastArgIndex)));
} else {
console.log(err);
}
});
return serverInstance;
}
listen(serverOrExpressApp, [port[, host[, backlog]]][, callback])
就像按照
https://nodejs.org/api/net.html.net_server_listen_port_host_backlog_callback
回调签名更改为
(端口)=>无效
const server = listen(app, 3000, (port) => {
console.log("server is running on port :" + port);
});
// _____________ another example port and host
const server = listen(app, 3000, 'localhost', (port) => {
console.log("server is running on port :" + port);
});
与旧例相反! 此方法不会调用自身!
关键要素:
重要的
serverInstance.listen.apply(serverInstance, [port].concat(args.slice(2, lastArgIndex)));
为什么我们在这里跳过回调?
添加后的回调。 它保存在数组内部的服务器实例中! 如果我们再加一个! 我们将有多个触发器! 关于(尝试+ 1)的次数。 所以我们只在第一次尝试中包含它!
这样我们就可以直接返回服务器实例! 并继续使用它来尝试! 而且做得很干净!
这也有助于一目了然地更好地理解
function listen(server, port, callback) {
const serverInstance = server.listen(port, () => { callback(port) })
.on('error', function (err) {
if(err.errno === 'EADDRINUSE') {
console.log(`----- Port ${port} is busy, trying with port ${port + 1} -----`);
port += 1;
serverInstance.listen(port);
} else {
console.log(err);
}
});
return serverInstance;
}
这里把参数端口变量玩上了闭包!
function listen(server, ...args) {
// __________________________________ overriding the callback method (closure to pass port)
const lastArgIndex = args.length - 1;
let port = args[0];
if (typeof args[lastArgIndex] === 'function') {
const callback = args[lastArgIndex];
args[lastArgIndex] = function () {
callback(port);
}
}
const serverInstance = server.listen(server, ...args)
.on('error', function (err) {
if(err.errno === 'EADDRINUSE') {
console.log(`----- Port ${port} is busy, trying with port ${port + 1} -----`);
port += 1;
serverInstance.listen(...[port, ...args.slice(1, lastArgIndex)])
} else {
console.log(err);
}
});
return serverInstance;
}
说对了还真不是! 但是对于第一个版本! 我们在每次失败时调用函数本身! 每次它都会创建一个新实例! 垃圾收集器会让一些肌肉动起来!
没关系,因为这个函数只在开始时执行一次!
旧版本没有返回服务器实例!
你可以看看@sakib11 的评论,看看他遇到了什么问题! 可以考虑周到!
在评论中我还提到了 promise 版本和闭包 getter 模式! 我不认为他们有趣! 上面的方式只是尊重与 nodejs 相同的签名! 而且回调就好了! 我们正在将我们的服务器引用写掉! 有承诺的版本! 返回一个承诺,并且在决议中我们通过了所有元素! 服务器实例+端口!
如果您想知道闭包吸气剂模式! (这里不好)
在我们的方法中,我们创建了一个引用服务器实例的 ref! 如果我们不能像我们正在做的那样返回服务器实例(想象这是不可能的!所以每次创建一个新实例!该模式包括创建一个闭包(该范围内的方法)并返回它!
所以为了使用
const getServer = listen(port, () => {
console.log('Server running at port ' + getServer().address().port);
const io = socketIo(getServer(), {});
});
但这只是开销,特别是我们需要等待服务器完成! 除非我们以使用回调的方式设置它! 或回报承诺!
而且这太复杂了! 而且一点都不好!
只是因为我提到了它!
上面的方法可以调整! 添加尝试次数限制! 并添加一些事件或钩子! 但是好吧! 一般我们只需要一个简单的函数就可以尝试实现了! 对我来说,以上就足够了!
从文档
app.listen() 方法返回一个 http.Server 对象,并且(对于 HTTP)是以下内容的便捷方法:
app.listen = function () {
var server = http.createServer(this)
return server.listen.apply(server, arguments)
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.