Node.js Express 应用程序处理启动错误
Posted
技术标签:
【中文标题】Node.js Express 应用程序处理启动错误【英文标题】:Node.js Express app handle startup errors 【发布时间】:2012-10-30 15:31:15 【问题描述】:我在 Node.js 和 Express 中有应用程序。我需要为它编写测试。我在处理 Express 应用程序错误时遇到问题。我找到了这个How do I catch node.js/express server errors like EADDRINUSE?,但它对我不起作用,我不知道为什么。我想处理 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.on 'error', ...
应该可以工作。即使该行存在,它是否只是进行正常的堆栈跟踪并崩溃?
是的,如果我这样做'listener.listen(80)',它会打印堆栈跟踪并崩溃。即使使用 'listener.on 'error', ...' 也许在这种情况下发生的错误不是 Express 错误,这就是它无法处理的原因。但这只是假设。
【参考方案1】:
这应该可以解决问题:
listener.listen(80).on('error', function(err) );
listener.listen
实际上所做的是创建一个 HTTP 服务器并在其上调用监听:
app.listen = function()
var server = http.createServer(this);
return server.listen.apply(server, arguments);
;
【讨论】:
令人难以置信的是,这是最简单、最正确的答案,而且没有人赞成!诀窍是了解 webServer 对象是由 app.listen 返回的。我用这个:app.listen(80, function() console.log('success'); ).on('error', function(err) if (err.errno === 'EADDRINUSE') console.log('port busy'); else console.log(err); );
我不明白你的代码在做什么。如果你调用 app.listen 节点已经在我认为的引擎盖下做了一个 http.createServer 或者你基本上覆盖了原生 app.listen() 方法并用你自己的代码覆盖它?
第一行代码是应该使用的,其他都是解释为什么会这样。【参考方案2】:
首先,expressJS 不会抛出 uncaughtException
事件,进程会,所以你的代码不起作用也就不足为奇了。
所以请改用:process.on('uncaughtException',handler)
。
接下来,expressJS 已经提供了一种标准的错误处理方法,即使用它为此目的提供的中间件函数,如下所示:
app.configure(function()
app.use(express.errorHandler( dumpExceptions: true, showStack: true ));
);
此函数向客户端返回一条错误消息,带有可选的堆栈跟踪,并记录在 connectJS errorHandler。
(注意errorHandler其实是connectJS的一部分,只被expressJS暴露出来。)
如果现有的 errorHandler 提供的行为不足以满足您的需求,则其源位于 connectJS's errorHandler
middleware 并且可以轻松修改以满足您的需求。
当然,与其直接修改这个函数,“正确”的做法是创建自己的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, Already 了解 connectJS 对 filter
和 provider
中间件的想法的解释,以及 How To Write Middleware for Connect/Express 以获得编写良好的教程。
您可能还会发现这些有用:
How to handle code exceptions in node.js?
Recover from Uncaught Exception in Node.JS
最后,可以在its own tests. 中找到有关测试 expressJS 的极好信息来源
【讨论】:
感谢您提供如此详细的解释,无论如何它都会很有用。在处理 Web 请求时,我不需要处理错误。我需要同时创建服务器并调用 app.listen(...)。现在我明白这里需要 Node.js 错误处理,而不是 Express 中的中间件。 process.on('uncaughtException',handler) 在我的情况下是可以接受的。 @Piane_Ramso,然后是的,将您自己的事件处理程序添加到process.on('uncaughtException')
绝对是要走的路。如果您很好奇,处理“未捕获”异常的底层代码位于(或附近)node.cc 中的第 1739 行(或附近)
天哪,这很复杂【参考方案3】:
提及:Marius Tibeica 回答完整而精彩,david_p 评论也是。 Rob Raisch 的回答也是如此(有趣的探索)。 https://***.com/a/27040451/7668448https://***.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递归调用自身。在端口繁忙错误的情况下。每次增加端口号。
更新完全重做
回调完整版
首先这个版本是跟nodejshttp.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);
);
解释
与旧示例相反!此方法不调用自身!
关键要素:
app.listen() 第一次调用会返回一个 net.Server 实例 绑定一次事件后,再次调用listen到同一个网络。服务器实例将尝试重新连接! 错误事件监听器始终存在! 每次发生错误时,我们都会重新尝试。 端口变量播放关闭回调!当回调将被调用时,将传递正确的值。很重要
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;
这里参数端口变量play就关闭了!
ES6 完整版
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)
你可以看看@sakib11 的评论,看看他遇到了什么问题!可以考虑周到!
我还在评论中提到了承诺版本和闭包吸气剂模式!我不认为他们有趣!上面的方式只是尊重与nodejs相同的签名!而且回调就好了!而且我们正在将我们的服务器参考写掉!带有承诺版本!一个承诺得到回报,我们通过了所有的元素!服务器实例 + 端口!
如果你想知道闭包吸气剂模式! (这里不好)
在我们的方法中,我们创建了一个引用服务器实例的 ref!如果我们不能像我们正在做的那样返回服务器实例(想象这是不可能的!所以每次创建一个新实例!该模式包括创建一个闭包(在该范围内的方法)并返回它!
所以使用
const getServer = listen(port, () =>
console.log('Server running at port ' + getServer().address().port);
const io = socketIo(getServer(), );
);
但这只是开销,特别是我们需要等待服务器完成! 除非我们以使用回调的方式设置它!或返回一个承诺!
而且它只是过于复杂了!而且一点都不好!
只是因为我提到了它!
上面的方法可以调整!添加尝试次数限制!并添加一些事件或钩子!不过好!通常我们只需要一个简单的函数来尝试并实现它!对我来说,以上就足够了!
好的链接
https://nodejs.org/api/http.html#http_http_createserver_options_requestlistener
https://nodejs.org/api/http.html#http_class_http_server
https://expressjs.com/en/4x/api.html#app.listen
来自文档
app.listen() 方法返回一个 http.Server 对象,并且(对于 HTTP)是一种方便的方法:
app.listen = function ()
var server = http.createServer(this)
return server.listen.apply(server, arguments)
【讨论】:
完美解决我的问题,谢谢兄弟 很高兴听到这个。我的荣幸。谢谢 我正在尝试类似的东西,递归位的工作方式与此完全相同,但我也试图返回 app.listen 以将其传递给 socket.io,但由于某种原因它不起作用。我猜附加错误事件处理程序会返回不同的东西? 你是怎么返回值的!在我的示例中,如果我们返回第一个 app.listen().on(),我们将获得一个服务器实例!但是如果失败了!重试的回调之一将被调用!并且将创建一个新的服务器实例!所以这应该是你的问题! app.listen 和所有的 .on 链接都将返回一个 net.Server 实例!您可以将其附加到 socket.io!你主要有两种选择!使用 http.createServer。获取您的服务器实例!然后在我的函数中代替 app.listen 使用 server.listen!并将其附加到socket.io!你有参考! 否则,如果我们想在函数本身内执行它!那么我们有很多选择!使用 onSuccess 回调或 promise 可能是最简单的!您在解析时通过了成功连接的服务器!以上是关于Node.js Express 应用程序处理启动错误的主要内容,如果未能解决你的问题,请参考以下文章
node.js + express.js:使用 mongodb/mongoose 处理会话