Node.js之错误处理
Node.js之错误处理
1. 使用 domain 模块处理错误
try..catch
多用于捕捉同步方法中的抛出错误,但不能用try..catch捕捉异步方法中抛出de错误
如:
1 var http = require('http')
2 try{
3 http.createServer(function(req,res){
4 if(req.url!="/favicon.ico"){
5 noneexist();//不存在本函数
6 res.writeHead(200,{'Content-Type':'text/html'})
7 res.write('<head><meta charset="utf-8"/></head>')
8 res.end('你好\n')
9 }
10 }).listen(8000,"127.0.0.1")
11 }
12 catch(err){
13 console.log('接收客户端请求时发生以下错误:')
14 console.log(err.code)
15 }
在用浏览器请求时,会出错,因为noneexist()不存在,程序也会停止运行
$ node app.js
D:\Users\yuan\Desktop\app.js:6
noneexist();//不存在本函数
^
ReferenceError: noneexist is not defined
at Server.<anonymous> (D:\Users\yuan\Desktop\app.js:6:13)
at emitTwo (events.js:125:13)
at Server.emit (events.js:213:7)
at parserOnIncoming (_http_server.js:602:12)
at HTTPParser.parserOnHeadersComplete (_http_common.js:116:23)
为了防止程序被强制关闭,提供了一个uncaughtException事件,用于捕捉及处理任何未被处理的错误
可将代码改成如下形式:
var http = require('http')
var http = require('http')
http.createServer(function(req,res){
if(req.url!="/favicon.ico"){
noneexist();//不存在本函数
res.writeHead(200,{'Content-Type':'text/html'})
res.write('<head><meta charset="utf-8"/></head>')
res.end('你好\n')
}
}).listen(8000,"127.0.0.1")
process.on('uncaughtException',function(err){
console.log('接收客户端请求时发生以下错误:')
console.log(err)
})
运行结果如下会报错,但不会停止:
$ node app.js
接收客户端请求时发生以下错误:
ReferenceError: noneexist is not defined
at Server.<anonymous> (D:\Users\yuan\Desktop\app.js:6:13)
at emitTwo (events.js:125:13)
at Server.emit (events.js:213:7)
at parserOnIncoming (_http_server.js:602:12)
at HTTPParser.parserOnHeadersComplete (_http_common.js:116:23)
|
但这种方法可能会出现资源泄露,因此利用domain模块进行处理错误,代码如下:
var http = require('http')
var domain = require('domain')
http.createServer(function(req,res){
var d = domain.create()
d.once('error',function(err){
res.writeHead(200,{'Content-Type':'text/html'})
res.write('<head><meta charset="utf-8"/></head>')
res.write('服务器接收客户端请求时发生以下错误: ')
res.end(err.message)
})
d.run(function(){
if(req.url!=="/favicon.ico"){
nonexist();
res.writeHead(200,{'Content-Type':'text/html'})
res.write('<head><meta charset="utf-8"/></head>')
res.end('你好\n')
}
})
}).listen(8000,"127.0.0.1")
浏览器访问后在浏览器中会出现错误信息,程序不会间断
下面详细介绍Domain
2. 创建并使用Domain
-
创建domain对象:
var domain= domain.create();
-
当该对象捕获到任何错误信息时,触发该对象的error事件,可以通过对监听对象的error事件并指定事件回调函数的方法来实现对捕捉到错误时的处理。
domain.on('error',function(err){ //事件回调函数代码 })
-
在domain对象中定义了一个name属性值,用于获取Domain对象的名字
domain。name
-
Domain对象被创建后,利用run方法指定Domain监视对象
domain。run(fn)//fn为一函数
eg:
var domain = require('domain')
var fs = require('fs')
var d = domain.create()
d.name = 'dl'
d.on('error',function(err){
console.error(d.name,err)
})
d.run(function(){
process.nextTick(function(){
setTimeout(function(){
fs.open('non-existent file','r',function(err,fd){
if(err)throw err
})
},1000)
})
})
输出结果为:
$ node app.js
dl { Error: ENOENT: no such file or directory, open 'D:\Users\yuan\Desktop \non-existent file'
errno: -4058,
code: 'ENOENT',
syscall: 'open',
path: 'D:\\Users\\yuan\\Desktop\\non-existent file',
domain:
Domain {
domain: null,
_events: { error: [Function] },
_eventsCount: 1,
_maxListeners: undefined,
members: [],
name: 'dl' },
domainThrown: true }
3. 隐式绑定与显示绑定
当使用Domain对象的run方法指定所有监听的函数时,函数中使用的实例对象都隐式地绑定到Domain对象上。有时需要某些对象不绑定Domain上,或者绑定到另一个Domain上,因此需要显性绑定,利用add方法
domain.add(emitter)
可通过下面例子说明,我们创建一个Http服务器,并指定服务器接收到的客户端请求时,首先创建一个Domain对象,然后使用该对象的add方法分别将用于读取客户端请求数据的http.IncomingMessage对象,与用于发送服务器端响应数据的http.ServerRespanse对象绑定到Domain对象上,并制定当Domain对象那个捕获到错误时客户端返回错误信息。
1 var http = require('http')
2 var domain = require('domain')
3 http.createServer(function(req,res){
4 var d = domain.create();
5 d.add(req)
6 d.add(res)
7 d.on('error',function(err){
8
9 res.writeHead(200)
10 res.write('服务器接收客户端请求时发生以下错误: ')
11 res.end(err.message)
12
13 })
14
15 res.writeHead(200)
16 req.on('data',function(){
17 bibeexists();
18 res.write('你好')
19 res.end()
20 })
21
22 }).listen(8000)
接下来创建一个用于向Http服务器提供请求的模块文件代码
1 var http = require('http')
2 var options = {
3 hostname:'localhost',
4 port:8888,
5 path:'/',
6 method:'POST'
7 }
8 var req = http.request(options,function(res){
9 res.setEncoding('utf8')
10 res.on('data',function(chunk){
11 console.log('响应内容:' + chunk)
12 })
13 })
14 req.write('你好')
15 req.end('再见')
分别在不同界面运行以上两个程序,可得,服务器不停止工作,http请求端返回错误数据:
[root@kuber2 webproject]# node client.js
响应内容:服务器接收客户端请求时发生以下错误:
响应内容:noneexists is not defined
[root@kuber2 webproject]#
使用了add方法绑定了Domain,可以使用remove方法解绑,即
domain.remove(emitter)
4 绑定回调函数与拦截回调函数
可以利用bind方法将一个回调函数与Domain绑定在一起
domain.bind(callback)
回调函数中如果有异常将异常抛出给domain,但本方法必须有throw err
eg:
1 var fs = require('fs')
2 var domain = require('domain')
3 var d = domain.create()
4 fs.readFile('./test.txt',d.bind(function(err,data){
5 if(err) {
6
7 console.log('blind绑定的回调函数抛出的异常:' + err.message)
8 throw err
9 }
10 else console.log(data)
11 })
12 )
13 d.on('error',function(err){
14 console.log('读取文件时发生以下错误:')
15 console.log(err)
16 })
运行所得结果如下:
blind绑定的回调函数抛出的异常:ENOENT: no such file or directory, open './test.txt'
读取文件时发生以下错误:
{ Error: ENOENT: no such file or directory, open './test.txt'
at Error (native)
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: './test.txt',
domain:
Domain {
domain: null,
_events: { error: [Function] },
_eventsCount: 1,
_maxListeners: undefined,
members: [] },
domainThrown: true }
[root@kuber2 webproject]#
使用intercept可以拦截回调函数的异常,使用方法:
domain.intercept(callback)
eg:
1 var fs = require('fs')
2 var domain = require('domain')
3 var d = domain.create()
4 fs.readFile('./test.txt',d.intercept(function(err,data){
5 console.log(data)
6 })
7 )
8 d.on('error',function(err){
9 console.log('读取文件时发生以下错误:')
10 console.log(err)
11 })
运行结果:
[root@kuber2 webproject]# node app1.js
读取文件时发生以下错误:
{ Error: ENOENT: no such file or directory, open './test.txt'
at Error (native)
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: './test.txt',
domain:
Domain {
domain: null,
_events: { error: [Function] },
_eventsCount: 1,
_maxListeners: undefined,
members: [] },
domainThrown: false,
domainBound: [Function] }
[root@kuber2 webproject]#
5 domain堆栈的弹出与推入
当使用run,bind,intercept方法监听函数时,将把Domain对象推入domai堆栈中,可利用_stack属性查看Domain堆栈内容,堆栈中只能存放一个对象,后这会将前者挤出堆栈。
利用下列代码进行说明:
1 var domain = require('domain')
2 var d1 = domain.create();
3 d1.name = "d1"
4 var d2 = domain.create();
5 d2.name = 'd2'
6 console.log('原始堆栈:')
7 console.log(domain._stack)
8 d1.run(function(){
9 console.log('d1对象:')
10 console.log(d1)
11 console.log('运行d1对象后的堆栈内容:')
12 console.log(domain._stack)
13 })
14 d2.run(function(){
15 console.log('d2对象:')
16 console.log(d2)
17 console.log('运行d2对象后的堆栈内容:')
18 console.log(domain._stack)
19 })
执行结果如下:
[root@kuber2 webproject]# node app2.js
原始堆栈:
[]
d1对象:
Domain {
domain: null,
_events: {},
_eventsCount: 0,
_maxListeners: undefined,
members: [],
name: 'd1' }
运行d1对象后的堆栈内容:
[ Domain {
domain: null,
_events: {},
_eventsCount: 0,
_maxListeners: undefined,
members: [],
name: 'd1' } ]
d2对象:
Domain {
domain: null,
_events: {},
_eventsCount: 0,
_maxListeners: undefined,
members: [],
name: 'd2' }
运行d2对象后的堆栈内容:
[ Domain {
domain: null,
_events: {},
_eventsCount: 0,
_maxListeners: undefined,
members: [],
name: 'd2' } ]
[root@kuber2 webproject]#
可以利用exit方法将Domain对象从domain堆栈中弹出,弹出后不能再捕获错误
eg:
1 var domain = require('domain')
2 var d = domain.create()
3 d.on('error',function(err){
4 console.log('Domain对象捕获到错误')
5 })
6 console.log('原始堆栈:')
7 console.log(domain._stack)
8 d.run(function(){
9 console.log('运行domain对象后的堆栈内容:')
10 console.log(domain._stack)
11 throw new Error("error")
12 })
此时输出结果为:
[root@kuber2 webproject]# node app3.js
原始堆栈:
[]
运行domain对象后的堆栈内容:
[ Domain {
domain: null,
_events: { error: [Function] },
_eventsCount: 1,
_maxListeners: undefined,
members: [] } ]
Domain对象捕获到错误
[root@kuber2 webproject]#
将domain对象从堆栈中弹出:
1 var domain = require('domain')
2 var d = domain.create()
3 d.on('error',function(err){
4 console.log('Domain对象捕获到错误')
5 })
6 console.log('原始堆栈:')
7 console.log(domain._stack)
8 d.run(function(){
9 d.exit();
10 console.log('运行domain对象后的堆栈内容:')
11 console.log(domain._stack)
12 // throw new Error("error")
13 })
14
输出结果为:
[root@kuber2 webproject]# node app3.js
原始堆栈:
[]
运行domain对象后的堆栈内容:
[]
domain对象中抛出异常会是程序停止:
1 var domain = require('domain')
2 var d = domain.create()
3 d.on('error',function(err){
4 console.log('Domain对象捕获到错误')
5 })
6 console.log('原始堆栈:')
7 console.log(domain._stack)
8 d.run(function(){
9 d.exit();
10 console.log('运行domain对象后的堆栈内容:')
11 console.log(domain._stack)
12 throw new Error("error")
13 })
14
输出结果从,程序停止:
/root/webproject/app3.js:12
throw new Error("error")
^
Error: error
at Domain.<anonymous> (/root/webproject/app3.js:12:8)
at Domain.run (domain.js:221:14)
at Object.<anonymous> (/root/webproject/app3.js:8:3)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.runMain (module.js:604:10)
at run (bootstrap_node.js:389:7)
[root@kuber2 webproject]#
可以使用Domain对象的enter方法将一个Domain对象推入domain堆栈中并使该Domain对象变成当前使用的Domain对象
继续修改上述代码加入enter方法:
1 var domain = require('domain')
2 var d = domain.create()
3 d.on('error',function(err){
4 console.log('Domain对象捕获到错误')
5 })
6 console.log('原始堆栈:')
7 console.log(domain._stack)
8 d.run(function(){
9 d.exit();
10 console.log('运行domain对象后的堆栈内容:')
11 console.log(domain._stack)
12 d.enter()
13 console.log('运行enter方法后的堆栈内容:')
14 console.log(domain._stack)
15 throw new Error("error")
16 })
17
运行结果:
root@kuber2 webproject]# node app3.js
原始堆栈:
[]
运行domain对象后的堆栈内容:
[]
运行enter方法后的堆栈内容:
[ Domain {
domain: null,
_events: { error: [Function] },
_eventsCount: 1,
_maxListeners: undefined,
members: [] } ]
Domain对象捕获到错误
[root@kuber2 webproject]#
如果多个Domain对象存在嵌套,则会抛出最内层的异常。如果外层用exit方法弹出,则虽有的Domain对象都会被弹出
6. Domain对象的销毁
在一个Domain对象不再使用时,可对其进行销毁
d.dispose()