Node.js下自定义错误类型

Node.js下自定义错误类型

Node.js下自定义错误类型

在JavaScript里面,运行过程中的错误的类型总是被人忽略,这篇教程主要从三个方面来介绍如何在Node.js下自定义错误类型。

  • 为什么要使用错误对象。
  • 怎么创建自定义错误对象。
  • 一些自定义错误对象的例子。

为什么要使用错误对象

一般来说,很少人会考虑如何处理应用产生的错误的策略,调试的过程中,简单地利用console.log(‘error’)定位错误,基本够用了,通过留下这些调试信息,能够为我们以后的调试过程中升了不少时间,提高了维护性。所以错误提示非常重要。同时,也会带来一些比较糟糕用法。

糟糕的字符串错误提示

Guillermo Rauch 曾经写了一篇优秀的文章解释为什么最好不要用字符串作为错误调信息,以下是一种常见的错误处理方式

if (id < 0) return ‘id must be positive’

直接返回错误字符串信息,虽然很简单,但是项目大了,就不好维护了,会产生以下这几个问题。

很难知道错误在哪里发生的?

除非你的打印字符串里面,包含了文件和代码的行数,要不然,你根本不知道错误在哪里发生的。

####不能处理不同类型的错误

如果全部都是使用字符串作为错误提示,在运行的过程中,很难针对不同类型的错误进行不同的处理,有时候我们需要针对不同的错误进行不同类型的处理,如提交表单信息错误,数据库类型错误等。

系统错误对象

利用系统自带的错误类型来代替纯字符串的错误提示

if (id < 0) return new Error('id must be positive')

尽管看上去只是多了一点小步骤,但实际上却带了了很多好处。

获取错误在哪里发生

如果用了错误,对象会产生堆栈属性,通过执行文件,通过行号,查找到出错的地方。

console.log(error.stack)
Error: An error occurred
    at Object.<anonymous>(/Users/ds/main.js:1:73)
    at Module._compile (module.js:441:26)
    at Object..js (module.js:459:10)
    at Module.load (module.js:348:31)
    at Function._load (module.js:308:12)
    at Array.0 (module.js:479:10)
    at EventEmitter._tickCallback (node.js:192:40)

获取错误类型

可以通过Instanceof 来检查错误类型,根据类型进行不同的处理

if (err instanceof DatabaseError) { /* do this */ }
if (err instanceof AuthenticationError) { /* do that */ }

如何创建一个自定义的错误对象

通过对错误对象的继承与复写的手段,创建一个自定义的错误对象。

抽象错误对象类型

创建一个抽象的错误类基类

var util = require('util')

var AbstractError = function (msg, constr) { Error.captureStackTrace(this, constr || this) this.message = msg || ‘Error’ } util.inherits(AbstractError, Error) AbstractError.prototype.name = ‘Abstract Error’

定义数据库错误对象

利用上述创建的抽象错误类型,扩展到其他自定义错误类型当中

var DatabaseError = function (msg) {
  DatabaseError.super_.call(this, msg, this.constructor)
}
util.inherits(DatabaseError, AbstractError)
DatabaseError.prototype.message = 'Database Error'

错误类型部署到应用当中,下例

function getUserById(id, callback) {
  if (!id) {
     return callback(new Error('Id is required'))
  }
  // Let’s pretend our database breaks if we try to
  // find a user with an id higher than 10
  if (id > 10) {
    return callback(new DatabaseError(Id can't be higher ↵ than 10))
  }
  callback(null, { name: 'Harry Goldfarb' })
}

function onGetUserById(err, resp) { if (err) { return console.log(err.toString()) } console.log(‘Success:’, resp.name) }

getUserById(1, onGetUserById) // Harry Goldfarb getUserById(null, onGetUserById) // Error: Id is required getUserById(53, onGetUserById) // Database Error: Id can’t be higher than 10

效果出来了,我们的getUserById 的方法,现在可以返回两种不同类型的错误,一个是原生的错误类型,一个是自定义的数据库错误类型databaseerror,如果我们调用toString,我们可以看到错误类型的返回

// start our script in production mode
$ NODE_ENV=production node main.js


function onGetUserById(err, resp) {
  if (err) {
    if (err instanceof DatabaseError &&
        process.env.NODE_ENV != 'production') {
      return console.log(err)
    }
    return console.log('Sorry there was an error')
  }
  console.log(resp.name)
}

根据不同错误类型进行不同情况的处理

复用错误类型

我们不需要每次都重新定义我们的自定义错误在每一个文件里面,我们只需要创建一个文件,调用一个很重要的node.js方法require就足够了

// in ApplicationErrors.js
var util = require('util')

var AbstractError = function (msg, constr) { Error.captureStackTrace(this, constr || this) this.message = msg || ‘Error’ } util.inherits(AbstractError, Error) AbstractError.prototype.name = ‘Abstract Error’

var DatabaseError = function (msg) { DatabaseError.super_.call(this, msg, this.constructor) } util.inherits(DatabaseError, AbstractError) DatabaseError.prototype.name = ‘Database Error’

module.exports = { Database: DatabaseError }

// in main.js var ApplicationError = require(’./ApplicationErrors’)

function getUserById(id, callback) { if (!id) { return callback(new Error(‘Id is required’)) }

if (id > 10) { return callback(new ApplicationError.Database(‘Id cant ↵ be higher than 10’)) }

callback(null, { name: ‘Harry Goldfarb’ }) }

由此可见,我们只需要定义一次,其他地方也能使用错误对象类型。

原文:Using Custom Errors in Node.js


15 回复

Node.js 下自定义错误类型

在JavaScript中,处理运行时错误的类型往往被忽视。这篇文章将从三个方面介绍如何在Node.js中自定义错误类型。

为什么要使用错误对象?

通常情况下,开发者在调试时可能会简单地使用console.log('error')来记录错误,这种方式确实可以在一定程度上帮助定位问题。然而,随着项目的复杂度增加,这种做法会变得难以维护。以下是几个原因:

  • 难以定位错误发生的位置:如果错误信息中没有包含文件名和行号,那么很难快速定位错误。
  • 无法区分不同类型的错误:如果所有错误都使用字符串表示,那么在运行时很难对不同类型的错误进行针对性处理。

系统错误对象

相比于简单的字符串错误提示,使用系统自带的错误类型能带来更多的好处。例如,使用Error对象可以方便地获取错误的堆栈信息,并且可以通过instanceof来区分不同的错误类型。

if (id < 0) return new Error('id must be positive');

这样做的好处包括:

  • 获取错误发生位置:通过error.stack可以查看错误的堆栈信息,从而更容易定位错误发生的具体位置。

    console.log(error.stack);
    
  • 获取错误类型:通过instanceof操作符可以检查错误类型,并据此进行不同的处理。

    if (err instanceof DatabaseError) { /* do this */ }
    if (err instanceof AuthenticationError) { /* do that */ }
    

如何创建一个自定义的错误对象

为了创建自定义的错误对象,可以通过继承Error对象并覆盖其构造函数来实现。

抽象错误对象类型

首先,创建一个抽象的错误类作为基类。

var util = require('util');

var AbstractError = function (msg, constr) {
  Error.captureStackTrace(this, constr || this);
  this.message = msg || 'Error';
};
util.inherits(AbstractError, Error);
AbstractError.prototype.name = 'Abstract Error';
定义数据库错误对象

基于抽象错误类,我们可以进一步创建具体的错误类型,例如数据库错误。

var DatabaseError = function (msg) {
  DatabaseError.super_.call(this, msg, this.constructor);
};
util.inherits(DatabaseError, AbstractError);
DatabaseError.prototype.name = 'Database Error';

接下来,我们可以在实际的应用逻辑中使用这些自定义的错误类型。

function getUserById(id, callback) {
  if (!id) {
    return callback(new Error('Id is required'));
  }
  if (id > 10) {
    return callback(new DatabaseError('Id can\'t be higher than 10'));
  }
  callback(null, { name: 'Harry Goldfarb' });
}

function onGetUserById(err, resp) {
  if (err) {
    if (err instanceof DatabaseError && process.env.NODE_ENV !== 'production') {
      return console.log(err);
    }
    return console.log('Sorry there was an error');
  }
  console.log('Success:', resp.name);
}

getUserById(1, onGetUserById); // Harry Goldfarb
getUserById(null, onGetUserById); // Error: Id is required
getUserById(53, onGetUserById); 
// Database Error: Id can't be higher than 10

复用错误类型

为了避免每次在不同文件中重复定义自定义错误类型,我们可以将它们封装在一个单独的模块中,并通过require引入。

// 在 ApplicationErrors.js 文件中
var util = require('util');

var AbstractError = function (msg, constr) {
  Error.captureStackTrace(this, constr || this);
  this.message = msg || 'Error';
};
util.inherits(AbstractError, Error);
AbstractError.prototype.name = 'Abstract Error';

var DatabaseError = function (msg) {
  DatabaseError.super_.call(this, msg, this.constructor);
};
util.inherits(DatabaseError, AbstractError);
DatabaseError.prototype.name = 'Database Error';

module.exports = {
  Database: DatabaseError
};

// 在 main.js 文件中
var ApplicationError = require('./ApplicationErrors');

function getUserById(id, callback) {
  if (!id) {
    return callback(new Error('Id is required'));
  }

  if (id > 10) {
    return callback(new ApplicationError.Database('Id can\'t be higher than 10'));
  }

  callback(null, { name: 'Harry Goldfarb' });
}

通过这种方式,我们只需定义一次自定义错误类型,便可以在整个项目中复用这些类型。


赞一个

收藏了

先收藏。

mark

mark

好定西 私藏一下

对于Error.captureStackTrace的用法似乎有些问题:传入该函数的第二个参数值为constr || this,当constr不存在时,传入参数为this (具体的error对象)。但Error.captureStackTrace所接受的第二个参数应当为函数(在本文中则应当为error类的构造函数)。

因此,相关用法似乎应改为:Error.captureStackTrace(this, constr || this.constructor)

ES2015中已经可以直接继承Error类了。

感谢分享

在Node.js中,自定义错误类型能够帮助我们更好地管理和处理应用程序中的各种错误。以下是实现自定义错误类型的几个关键步骤:

1. 为什么要使用错误对象

使用错误对象相比于直接返回错误字符串有以下优势:

  • 更容易定位错误位置:错误对象通常带有堆栈跟踪信息。
  • 能区分不同类型错误:通过实例类型判断错误类型,从而进行针对性处理。

2. 创建自定义错误对象

首先,我们需要创建一个抽象的错误类作为基类:

var util = require('util');

var AbstractError = function (message) {
  Error.captureStackTrace(this, this.constructor);
  this.name = this.constructor.name;
  this.message = message || 'Error';
};
util.inherits(AbstractError, Error);

// 定义数据库错误对象
var DatabaseError = function (message) {
  DatabaseError.super_.call(this, message);
};
util.inherits(DatabaseError, AbstractError);
DatabaseError.prototype.name = 'DatabaseError';

// 使用自定义错误类型
function getUserById(id, callback) {
  if (!id) {
    return callback(new Error('Id is required'));
  }
  if (id > 10) {
    return callback(new DatabaseError('Id cannot be higher than 10'));
  }
  callback(null, { name: 'Harry Goldfarb' });
}

function onGetUserById(err, resp) {
  if (err) {
    if (err instanceof DatabaseError && process.env.NODE_ENV !== 'production') {
      console.log(err.stack);
    } else {
      console.log('Sorry, there was an error');
    }
    return;
  }
  console.log('Success:', resp.name);
}

getUserById(1, onGetUserById);   // Success: Harry Goldfarb
getUserById(null, onGetUserById); // Sorry, there was an error
getUserById(53, onGetUserById);   // Sorry, there was an error

3. 复用错误类型

为了方便在多个文件中复用这些错误类型,可以将它们定义在一个单独的模块中,并通过require引入:

// ApplicationErrors.js
var util = require('util');

function AbstractError(message) {
  Error.captureStackTrace(this, this.constructor);
  this.name = this.constructor.name;
  this.message = message || 'Error';
}
util.inherits(AbstractError, Error);

function DatabaseError(message) {
  DatabaseError.super_.call(this, message);
}
util.inherits(DatabaseError, AbstractError);
DatabaseError.prototype.name = 'DatabaseError';

module.exports = {
  DatabaseError: DatabaseError
};

然后在需要的地方引入并使用:

// main.js
var ApplicationErrors = require('./ApplicationErrors');

function getUserById(id, callback) {
  if (!id) {
    return callback(new Error('Id is required'));
  }
  if (id > 10) {
    return callback(new ApplicationErrors.DatabaseError('Id cannot be higher than 10'));
  }
  callback(null, { name: 'Harry Goldfarb' });
}

这样,我们就能够有效地自定义和管理错误类型了。

回到顶部