RxDB 是一个由 JavaScript 实现,拥有响应式,离线优先等特性的数据库。它拥有以下特性:
接下来本文将基于 RxDB 的 7.5.1 版本,简单分析一下 RxDB 内部的异常机制。
JavaScript 异常类型:
eval()
有关。eval()
在解析代码的过程中发生的语法错误。encodeURI()
或 decodeURl()
传递的参数无效。在 RxDB 内部主要定义了两个异常类:
利用 rx-error.js 文件提供的 newRxError 和 newRxTypeError 方法,我们可以方便地抛出自定义异常。在 encryption 插件内部,若数据库字段加密的密码的长度不为字符串或长度小于 8 时,则会抛出对应的异常。
具体的示例如下:
export const overwritable = {
validatePassword: function(password) {
if (password && typeof password !== 'string') {
throw RxError.newRxTypeError('EN1', {
password
});
}
if (password && password.length < minPassLength) {
throw RxError.newRxError('EN2', {
minPassLength,
password
});
}
}
};
这里我们先来分析 RxError.newRxTypeError:
export const newRxTypeError = (code, parameters) =>
new RxTypeError(code, overwritable.tunnelErrorMessage(code), parameters);
其中 RxTypeError 继承于 TypeError,该类的实现如下:
/*
* TypeError(类型错误)对象用来表示值的类型非预期类型时发生的错误。
* new TypeError([message[, fileName[, lineNumber]]])
* message:错误描述
* fileName:引起该异常代码所在的文件的名字
* lineNumber:引起该异常的代码的行号
*/
export class RxTypeError extends TypeError {
// 'EN1', { password }
constructor(code, message, parameters = {}) {
const mes = messageForError(message, parameters);
super(mes);
this.code = code;
this.message = mes;
this.parameters = parameters;
this.rxdb = true; // tag them as internal
}
get name() {
return 'RxError';
}
toString() {
return this.message;
}
get typeError() {
return true;
}
};
在 RxTypeError 构造函数内部,会调用 messageForError 函数,生成异常消息:
const messageForError = (message, parameters) => {
return 'RxError:' + '\n' +
message + '\n' +
parametersToString(parameters);
};
在 messageForError 函数内部,会进一步调用 parametersToString 函数进行参数对象
/**
* transform an object of parameters to a presentable string
* @param {any} parameters
* @return {string}
*/
const parametersToString = (parameters) => {
// { minPassLength, password }
let ret = '';
if (Object.keys(parameters).length === 0)
return ret;
ret += 'Given parameters: {\n';
ret += Object.keys(parameters) // [minPassLength, password]
.map(k => {
let paramStr = '[object Object]';
try {
paramStr = JSON.stringify(
parameters[k],
(k, v) => v === undefined ? null : v,
2
);
} catch (e) {}
return k + ':' + paramStr;
})
.join('\n');
ret += '}';
return ret;
};
JSON.stringify()
方法是将一个JavaScript值(对象或者数组)转换为一个 JSON 字符串,如果指定了 replacer 是一个函数,则可以替换值,或者如果指定了 replacer 是一个数组,可选的仅包括指定的属性。
关于序列化,有下面五点注意事项:
undefined
、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null
(出现在数组中时)。replacer
参数中强制指定包含了它们。JSON.stringify({}); // '{}'
JSON.stringify(true); // 'true'
JSON.stringify("foo"); // '"foo"'
JSON.stringify([1, "false", false]); // '[1,"false",false]'
JSON.stringify({ x: 5 }); // '{"x":5}'
JSON.stringify({x: 5, y: 6});
// "{"x":5,"y":6}"
JSON.stringify([new Number(1), new String("false"), new Boolean(false)]);
// '[1,"false",false]'
分析完 RxError.newRxTypeError,我们再来看一下 newRxError 函数。
export const newRxError = (code, parameters) =>
new RxError(code, overwritable.tunnelErrorMessage(code), parameters);
RxError 类继承于 Error 类,具体实现如下:
export class RxError extends Error {
constructor(code, message, parameters = {}) {
const mes = messageForError(message, parameters);
super(mes);
this.code = code;
this.message = mes;
this.parameters = parameters;
this.rxdb = true; // tag them as internal
}
get name() {
return 'RxError';
}
toString() {
return this.message;
}
get typeError() {
return false;
}
};
另外在创建异常消息时,内部会调用 overwritable.tunnelErrorMessage() 方法生成 code 对应的异常消息:
/**
* overwritte to map error-codes to text-messages
* @param {string} message
* @return {string}
*/
tunnelErrorMessage(message) {
// TODO better text with link
return `RxDB Error-Code ${message}.
- To find out what this means, use the error-messages-plugin
https://pubkey.github.io/rxdb/custom-build.html#error-messages
- Or search for this code https://github.com/pubkey/rxdb/search?
l=JavaScript&q=${message}%3A`;
}
不知道小伙伴有没有注意到 ‘EN1’ 和 ‘EN2’ 异常码,这些异常代码统一的定义在 error-message.js
文件的 CODES 对象中,该对象统一定义了 RxDB 中的所有异常信息:
const CODES = {
// util.js
UT1: 'given name is no string or empty',
UT2: `collection- and database-names must match the regex`
// plugins/encryption.js
EN1: 'password is no string',
EN2: 'validatePassword: min-length of password not complied',
}
此外在 error-message 插件内部会重写默认的 tunnelErrorMessage 方法,从而正常的显示异常消息:
export const overwritable = {
tunnelErrorMessage(code) {
if (!CODES[code]) {
console.error('RxDB: Error-Code not known: ' + code);
throw new Error('Error-Cdoe ' + code + ' not known, contact the maintainer');
}
return CODES[code];
}
};
最后再总结一下异常的处理流程:
RxError.newRxTypeError('EN1', { password })
。overwritable.tunnelErrorMessage(code)
方法,根据异常代码,找出对应的异常信息(异常信息按照功能进行分类,定义在 error-message.js 文件内)。RxTypeError
构造函数,创建 RxTypeError 对象。在 RxTypeError 构造函数内部,会对传入的参数对象进行序列化处理,然后与 code 对应的错误信息进行拼接,最终生成完整异常信息。