adoyle-h/Ero.js

View on GitHub
doc/README.zh-Hans.md

Summary

Maintainability
Test Coverage
# Ero.js
![Node Version][Node Version Image]
[![Npm Package Version][Npm Package Version Image]][Npm Package Version LINK]
[![License][License Image]][License LINK]
![NodeJS Package Dependencies][NodeJS Package Dependencies Link]
[![Build Status][Build Status Image]][Build Status Link]
[![Code Climate][Code Climate Image]][Code Climate Link]
[![Test Coverage][Test Coverage Image]][Test Coverage Link]

一个提供了一些简单的函数的类库,用于帮助你构建自定义错误。

## TOC

<!-- MarkdownTOC -->

- [安装 \(Installation\)](#安装-installation)
- [快速上手 \(Quick Start\)](#快速上手-quick-start)
- [特性 \(Feature\)](#特性-feature)
    - [规约错误类型及其属性值](#规约错误类型及其属性值)
    - [创建不捕获堆栈信息的错误实例](#创建不捕获堆栈信息的错误实例)
    - [链式继承错误信息](#链式继承错误信息)
    - [支持 sprintf 语法](#支持-sprintf-语法)
    - [附加元数据 \(additional metadata\)](#附加元数据-additional-metadata)
    - [创建错误实例时,更灵活地传参](#创建错误实例时,更灵活地传参)
    - [多种错误空间](#多种错误空间)
- [基本概念 \(Basic Concepts\)](#基本概念-basic-concepts)
    - [Ero 类](#ero-类)
    - [错误模板 \(Error Template\)](#错误模板-error-template)
    - [错误定义 \(Error Definitions\)](#错误定义-error-definitions)
    - [错误基类 \(BaseError\)](#错误基类-baseerror)
    - [错误类 \(Error Class\)](#错误类-error-class)
- [API](#api)
- [版本 \(Versioning\)](#版本-versioning)
- [版权声明 \(Copyright and License\)](#版权声明-copyright-and-license)

<!-- /MarkdownTOC -->


<a name="安装-installation"></a>
## 安装 (Installation)

`npm install --save ero`

<a name="快速上手-quick-start"></a>
## 快速上手 (Quick Start)

强烈推荐你二次封装 `ero` 库,以此来扩展出你自己的错误模块。

编辑一个文件。比如 `error.js`:

```js
// error.js
var Ero = require('ero');

// 定义一个错误模板,用来规范错误类定义 (error definition) 的属性
var errorTemplate = {
    // 下一行代表着每个错误类定义**必须**包含 code 字段,'The error code' 用来描述这个字段的作用
    code: 'The error code',
    /**
     * 下一行代表着每个错误类定义**可以**包含 captureStackTrace 字段,
     * message 字段用来描述这个字段的作用,
     * required 表示这个字段是否必须定义,
     * 当 required 为 false 时,需要提供 default 表示 captureStackTrace 的默认值。
     */
    captureStackTrace: {
        message: 'Whether capture error stack or not',
        required: false,
        default: true,
    },
    statusCode: {
        message: 'The status code of HTTP response',
        required: false,
        default: 500,
    },
    logLevel: {
        message: 'The level for your logging message',
        required: true,
    },
};

// 创建一系列错误类的定义
var definitions = {
    Error: {
        code: '001',
        logLevel: 'error',
    },
    RejectError: {
        code: '002',
        captureStackTrace: false,
        statusCode: 400,
        logLevel: false,
    },
    NotFoundError: {
        code: '003',
        captureStackTrace: false,
        statusCode: 404,
        logLevel: 'info',
    },
};

// 创建 ero 实例
var ero = new Ero({
    template: errorTemplate,
    definitions: definitions,
});

module.exports = ero;
```

在另一个文件中,引用你的错误模块:

```js
var ero = require('./error');
// 你能通过 ero.Errors 引用上面定义的 definitions 实现的错误类
var Errors = ero.Errors;
// 你能查看本 ero 实例的 BaseError,注意每个 Ero 实例的 BaseError 实际上是不同的,
// 不过大多数情况下你不会直接使用 BaseError
var BaseError = ero.BaseError;

// 使用你自定义的错误类
// 假设有一个元数据(meta)
var meta = {
    a: 1,
    b: [2, 3, 4],
    c: {}
}

// 创建一个错误实例,并把元数据与这个实例绑定
// 错误信息可以使用 sprintf 类似的语法,实际上它是通过 [alexei/sprintf.js](https://github.com/alexei/sprintf.js) 实现的
// 请看 http://adoyle.me/Ero.js/#!/api/BaseError 了解关于构造函数参数的更多信息
var e = new Errors.Error(meta, '%s is %s', 'something', 'wrong');

// 你可以得到错误实例的一些属性
console.log('message: ', e.message);
console.log('name: ', e.name);
console.log('stack: ', e.stack);
console.log('meta: ', e.meta);
console.log('code: ', e.code);
console.log('captureStackTrace: ', e.captureStackTrace);
console.log('statusCode: ', e.statusCode);
console.log('logLevel: ', e.logLevel);

// 你可以判断当前错误实例是否属于此 ero 空间,是否继承自 ero.BaseError
ero.isError(e);  // => true
ero.isError(new Error());  // => false
```

<a name="基本概念-basic-concepts"></a>

<a name="特性-feature"></a>
## 特性 (Feature)

<a name="规约错误类型及其属性值"></a>
### 规约错误类型及其属性值

通过 [`Template`](#错误模板-error-template) 规范错误类会有哪些额外属性,哪些属性是可选/必选。

通过 [`Definition`](#错误定义-error-definitions) 来规约有哪些可以使用的错误类,以及错误实例的默认属性值。

这样做的好处:

- 将同一类型的错误抽象成类 (class)。通过 `instanceof` 来判断错误实例的归属类型。
- 可以扩展除了 message 和 stack 以外的错误实例的属性。
- 提供一个文件来统一管理所有错误,方便查询,而不是任由不同的错误分散在项目代码的各个地方。
- 清晰查阅每类错误会有什么属性。
- 通过 `Template` 规范错误类型定义,防止编码错误。

<a name="创建不捕获堆栈信息的错误实例"></a>
### 创建不捕获堆栈信息的错误实例

`error.stack` 并不是必有属性。意味着 `var error = new Errors.SubError();` 时,可以不捕获错误堆栈。因为存在这样的场景,开发者需要创建错误实例,但不关心错误堆栈。
(仅当 `SubError` 的 `captureStackTrace` 属性的值为 `false` 时才不会捕获堆栈。具体实现机制请去看 [BaseError][])

另外,当 `captureStackTrace` 为 `true` 时,并不代表着创建 error 实例时就会去收集错误堆栈。  
根据 v8 引擎的特性,只有使用 error.stack 的时候才会去收集,这在 Ero.js 这个库里也是同样处理的,同时[嵌套错误](#嵌套错误信息)时也是同样的处理。

<a name="链式继承错误信息"></a>
### 链式继承错误信息

```js
var firstErr = new Error('the first error');
var secondErr = new Errors.BaseError(firstErr, 'the second error');
var thirdErr = new Errors.BaseError(secondErr, '%s is %s', 'something', 'wrong');
console.log(thirdErr.message);  // 三个错误的 message 将会串联起来
console.log(thirdErr.meta);  // secondMeta 和 thirdMeta 将会存储在 err.meta 中。同名的属性,最新的会覆盖老的
console.log(thirdErr.stack);  // 三个错误的堆栈信息将会串联起来
```

- stack 默认会使用 `'\n==== Pre-Error-Stack ====\n'` 连接多个 `error.stack`。
- message 默认会使用 `' && '` 来连接多个 `error.message`。
- meta,会合并多个 `error.meta`

你可以自定义 stack 和 message 的连接符。

例如调用 `ero.setErrorStackSeparator('\nFrom previous event:\n')`,输出 Promise 风格的堆栈格式。  
调用 `ero.setMessageConnector(', ')`,来代替默认的 `' && '` 连接多个错误信息。

<a name="支持-sprintf-语法"></a>
### 支持 sprintf 语法

错误信息可以使用 sprintf 类似的语法,实际上它是通过 [alexei/sprintf.js](https://github.com/alexei/sprintf.js) 实现的

```js
var err = new Errors.BaseError('%s is %s', 'something', 'wrong');
console.log(err.message); // => 'something is wrong'
```

<a name="附加元数据-additional-metadata"></a>
### 附加元数据 (additional metadata)

每个错误实例,除了 message、stack 以及其他附加属性,还绑定元数据 (meta)。

元数据是为了提供运行时的附加信息,比如运行时的上下文,请求参数等等。

你也许看到过有人将键值对放入以 `key=value` 的形式写入 message 里。  
一旦有了元数据,就无需将键值对写入 message 里,省去了 format 和提取数据的步骤。直接以 json 的形式附加在 `error.meta` 上。

<a name="创建错误实例时,更灵活地传参"></a>
### 创建错误实例时,更灵活地传参

你可以添加一些元数据:

```js
var meta = {a: 1, b: '2', c: [3], d: true};
var err = new Errors.BaseError(meta, '%s is %s', 'something', 'wrong');
console.log(err.meta);  // meta 将会存储在 err.meta 中
```

你可以结合上一个错误:

```js
var firstErr = new Error('the first error');
var secondMeta = {a: 1, b: 3};
var secondErr = new Errors.BaseError(firstErr, secondMeta, 'the second error');
var thirdMeta = {b: '2', c: [3], d: true};
var thirdErr = new Errors.BaseError(thirdMeta, secondErr, '%s is %s', 'something', 'wrong');
console.log(thirdErr.message);  // 三个错误的 message 将会串联起来
console.log(thirdErr.meta);  // secondMeta 和 thirdMeta 将会存储在 err.meta 中。同名的属性,最新的会覆盖老的
console.log(thirdErr.stack);  // 三个错误的堆栈信息将会串联起来
```

**err 和 meta 是顺序无关的,只要保证在 message 之前即可。**

当然,error、meta、message 都是可选参数:

```js
var err = new Errors.BaseError();
```

<a name="多种错误空间"></a>
### 多种错误空间

你可以创建多个 Ero 实例。

大多数情况下,如构建服务器应用,你只需要创建一个 Ero 实例来定义你自己的错误类型。  
对于框架级别的库,你可以创建多个 Ero,分别提供框架层的错误类,以及用户层的错误类。  
对于类库级别的库,个人觉得你不需要使用本项目,用 nodejs 自带的 Error 足矣。

**注意**,不同 Ero 实例中的 template、BaseError、Errors 都是互相独立的,因此 `ero.isCustomError` 只能判断当前 Ero 实例下的错误,**而不能判断其他 Ero 实例下定义的错误**。


<a name="基本概念-basic-concepts"></a>
## 基本概念 (Basic Concepts)

<a name="ero-类"></a>
### Ero 类

`new Ero()` 生成一个 `Ero` 实例,相当于一个存放错误类型的空间,同时这个空间提供一些工具方法。

Ero 空间有以下主要成员:

- [template](#错误模板-error-template)
- [BaseError](#错误基类-baseerror)
- [Errors](#错误类-error-class): 所有自定义的错误类都放在这里

每个 Ero 空间都是独立的,互不影响。

Ero 还提供一些工具函数,如 `Ero.isCustomError`,具体请看 [API 文档][API - Ero]。

<a name="错误模板-error-template"></a>
### 错误模板 (Error Template)

错误模板用于约束错误定义中的属性。  
如果错误定义中缺少某个模板中设定为必选的属性,则在 `ero` 初始化的时候就会报错,以提醒开发者。  
同时,模板也可以为所有错误定义的属性设定默认值,方便简化代码。

错误模板中的 `message`,是为了强制让开发者解释每个错误属性的含义,别无它用。

可以通过 `ero.template` 得到标准化后的错误模板。

<a name="错误定义-error-definitions"></a>
### 错误定义 (Error Definitions)

每一个错误定义 (Error Definition) 用于创建对应的错误类 (Error Class)。

每一个错误定义是由 `<错误名称>: <属性定义>` 组成的键值对。错误名称必须唯一。

属性定义是一个由许多键值对组成的对象 (Object),它将被赋值到对应错误类的原型链上,作为每个错误实例的默认值。


<a name="错误基类-baseerror"></a>
### 错误基类 (BaseError)

本类库提供了一个 BaseError 作为 Ero 空间内自定义错误类的基类。

这个基类有以下几个属性:

- meta: {Object} 错误的元数据
- message: {String} 错误信息
- [stack]: {String} 错误堆栈,只当 `captureStackTrace` 为 `true` 时存在
- captureStackTrace: {Boolean} 是否捕捉错误堆栈,默认为 `true`
- name: {String} 错误类的名字
- ERROR_STACK_SEPARATOR: {String} 多个错误堆栈之间的分隔符
- MESSAGE_CONNECTOR: {String} 多个错误信息之间的连接符

BaseError 可以通过 `ero.BaseError` 得到。

更多信息请看 [`API 文档 - BaseError`][API - BaseError]

<a name="错误类-error-class"></a>
### 错误类 (Error Class)
你提供的每个错误定义 (Error Definitions),将会生成对应的错误类。错误类都是继承自 [`BaseError`][BaseError] 这个基类。

`BaseError` 提供了功能丰富的构造函数,方便你在创建错误实例时,附加更多有用的信息。

错误类会放在 `ero.Errors` 中,默认包含 `ero.Errors.BaseError`。

<a name="api"></a>
## API

API 的具体使用,以及 README 中未提到的细节请看 [API 文档][API]。

<a name="版本-versioning"></a>
## 版本 (Versioning)

版本迭代遵循 SemVer 2.0.0 的规则。

*但是*,当主版本号是零(0.y.z),一切*随时*都可能有*不兼容的修改*。这处于开发初始阶段,其公共 API 是不稳定的。

**当它处于不稳定期间,你所安装的包版本号应该使用 `~`。**

关于 SemVer 的更多信息,请访问 http://semver.org/

<a name="版权声明-copyright-and-license"></a>
## 版权声明 (Copyright and License)

Copyright (c) 2015-2016 ADoyle. The project is licensed under the **Apache License Version 2.0**.

See the [LICENSE][] file for the specific language governing permissions and limitations under the License.

See the [NOTICE][] file distributed with this work for additional information regarding copyright ownership.


<!-- Links -->

[LICENSE]: ./LICENSE
[NOTICE]: ./NOTICE
[BaseError]: #错误基类-baseerror
[API]: http://adoyle.me/Ero.js/
[API - BaseError]: http://adoyle.me/Ero.js/#!/api/BaseError
[API - Ero]: http://adoyle.me/Ero.js/#!/api/Ero

<!-- links -->

[Node Version Image]: https://img.shields.io/node/v/ero.svg
[Npm Package Version Image]: https://img.shields.io/npm/v/ero.svg
[Npm Package Version LINK]: https://www.npmjs.com/package/ero
[License Image]: https://img.shields.io/npm/l/ero.svg
[License LINK]: https://github.com/adoyle-h/Ero.js/blob/master/LICENSE
[NodeJS Package Dependencies Link]: https://david-dm.org/adoyle-h/Ero.js.svg
[Build Status Image]: https://travis-ci.org/adoyle-h/Ero.js.svg?branch=master
[Build Status Link]: https://travis-ci.org/adoyle-h/Ero.js
[Code Climate Image]: https://codeclimate.com/github/adoyle-h/Ero.js/badges/gpa.svg
[Code Climate Link]: https://codeclimate.com/github/adoyle-h/Ero.js
[Test Coverage Image]: https://codeclimate.com/github/adoyle-h/Ero.js/badges/coverage.svg
[Test Coverage Link]: https://codeclimate.com/github/adoyle-h/Ero.js/coverage