http-errors是koa、express社区使用比较广泛的基础库,主要用于处理HTTP Error。
比如koa-router的使用:
notImplementedThrowable = new HttpError.NotImplemented();
notAllowedThrowable = new HttpError.MethodNotAllowed();
其他的使用这里建议大家自己看文档:github.com/jshttp/http…
源码
statuses
http-errors中使用了一个statuses库,它是一个处理http status的工具库。
比如通过status.codes获取所有的http状态码。
[
100, 101, 102, 103,
200, 201, 202, 203, 204, 205, 206, 207, 208, 226,
300, 301, 302, 303, 304, 305, 306, 307, 308,
400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 421, 422, 423, 424, 425, 426, 428, 429, 431, 451, 500, 501,
502, 503, 504, 505, 506, 507, 508, 509, 510, 511
]
再比如statuses[code],根据code获取对应的http code msg。
Continue 100
Switching Protocols 101
Processing 102
Early Hints 103
OK 200
Created 201
Accepted 202
Non-Authoritative Information 203
No Content 204
Reset Content 205
Partial Content 206
Multi-Status 207
Already Reported 208
IM Used 226
Multiple Choices 300
Moved Permanently 301
Found 302
See Other 303
Not Modified 304
Use Proxy 305
(Unused) 306
Temporary Redirect 307
Permanent Redirect 308
Bad Request 400
Unauthorized 401
Payment Required 402
Forbidden 403
Not Found 404
Method Not Allowed 405
Not Acceptable 406
Proxy Authentication Required 407
Request Timeout 408
Conflict 409
Gone 410
Length Required 411
Precondition Failed 412
Payload Too Large 413
URI Too Long 414
Unsupported Media Type 415
Range Not Satisfiable 416
Expectation Failed 417
I'm a teapot 418
Misdirected Request 421
Unprocessable Entity 422
Locked 423
Failed Dependency 424
Unordered Collection 425
Upgrade Required 426
Precondition Required 428
Too Many Requests 429
Request Header Fields Too Large 431
Unavailable For Legal Reasons 451
Internal Server Error 500
Not Implemented 501
Bad Gateway 502
Service Unavailable 503
Gateway Timeout 504
HTTP Version Not Supported 505
Variant Also Negotiates 506
Insufficient Storage 507
Loop Detected 508
Bandwidth Limit Exceeded 509
Not Extended 510
Network Authentication Required 511
inherits
inherits是isaacs大佬早期实现的一个简单JS继承的方案,兼容浏览器环境以及node环境。
try {
// node环境使用util.inherits
var util = require('util');
/* istanbul ignore next */
if (typeof util.inherits !== 'function') throw '';
module.exports = util.inherits;
} catch (e) {
// 浏览器环境自己实现了一个
/* istanbul ignore next */
module.exports = require('./inherits_browser.js');
}
对应的inherits_browser.js,这里也可以看看。
if (typeof Object.create === 'function') {
// 如果支持Object.create,参考node.js 'util' module 简单实现了一个inherits
module.exports = function inherits(ctor, superCtor) {
if (superCtor) {
// 将父类的构造函数写入到了子类的.super_属性中,在使用中可以通过该属性方便调用到父类的构造函数。
ctor.super_ = superCtor
// 原型继承
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
})
}
};
} else {
// old school shim for old browsers
module.exports = function inherits(ctor, superCtor) {
if (superCtor) {
ctor.super_ = superCtor
var TempCtor = function () {}
TempCtor.prototype = superCtor.prototype
ctor.prototype = new TempCtor()
ctor.prototype.constructor = ctor
}
}
}
setprototypeof
setprototypeof是一个关于Polyfill for Object.setPrototypeOf的库,跨平台,兼容到IE8。
'use strict'
/* eslint no-proto: 0 */
module.exports = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array ? setProtoOf : mixinProperties)
function setProtoOf (obj, proto) {
obj.__proto__ = proto
return obj
}
function mixinProperties (obj, proto) {
for (var prop in proto) {
if (!Object.prototype.hasOwnProperty.call(obj, prop)) {
obj[prop] = proto[prop]
}
}
return obj
}
http-errors
/*!
* http-errors
* Copyright(c) 2014 Jonathan Ong
* Copyright(c) 2016 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict'
/**
* Module dependencies.
* @private
*/
var deprecate = require('depd')('http-errors')
// Polyfill for Object.setPrototypeOf
var setPrototypeOf = require('setprototypeof')
var statuses = require('statuses')
var inherits = require('inherits')
var toIdentifier = require('toidentifier')
/**
* Module exports.
* @public
*/
module.exports = createError
module.exports.HttpError = createHttpErrorConstructor() // 生成抽象基类
module.exports.isHttpError = createIsHttpErrorFunction(module.exports.HttpError)
// Populate exports for all constructors
// statues.codes: 所有状态码的数组
// [
// 100, 101, 102, 103,
// 200, 201, 202, 203, 204, 205, 206, 207, 208, 226,
// 300, 301, 302, 303, 304, 305, 306, 307, 308,
// 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 421, 422, 423, 424, 425, 426, 428, 429, 431, 451, 500, 501,
// 502, 503, 504, 505, 506, 507, 508, 509, 510, 511
// ]
// 默认就执行
populateConstructorExports(module.exports, statuses.codes, module.exports.HttpError)
/**
* 4xx => 400、5xx => 500,即获取code的大类
* @private
*/
function codeClass (status) {
return Number(String(status).charAt(0) + '00')
}
/**
* 根据调用方传参、直接生成对应的HTTP Error实例
* @returns {Error}
* @public
*/
function createError () {
// so much arity going on ~_~
var err
var msg
// 默认状态码500
var status = 500
var props = {}
// 遍历参数、处理err、msg、status、props
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i]
if (arg instanceof Error) {
// 如果是Error实例、存储err、status
err = arg
// status: 状态码、这里status有可能是字符串
status = err.status || err.statusCode || status
continue
}
switch (typeof arg) {
case 'string':
// 如果是字符串、认为是error msg
msg = arg
break
case 'number':
// 如果是数字、认为是状态码
status = arg
if (i !== 0) {
// 由于api格式:createError([status], [error], [properties])
deprecate('non-first-argument status code; replace with createError(' + arg + ', ...)')
}
break
case 'object':
// 自定义属性 props
props = arg
break
}
}
// 提示 => 有效的状态码范围
if (typeof status === 'number' && (status < 400 || status >= 600)) {
deprecate('non-error status code; use only 4xx or 5xx status codes')
}
// 非有效的status兜底为500
if (typeof status !== 'number' ||
(!statuses[status] && (status < 400 || status >= 600))) {
status = 500
}
// populateConstructorExports()默认就会执行
// 根据status寻找对应的异常构造函数constructor
var HttpError = createError[status] || createError[codeClass(status)]
if (!err) {
// 没有err根据HttpError构造一个异常示例
err = HttpError
? new HttpError(msg)
: new Error(msg || statuses[status])
// 生成堆栈.stack、记得添加createError,那么createError之上包括自身在内的全部栈帧都会被生成的堆栈跟踪
Error.captureStackTrace(err, createError)
}
if (!HttpError || !(err instanceof HttpError) || err.status !== status) {
// 走到这,说明err是来自业务方自己定义传入的
// add properties to generic error
// .expose属性:可用于指示是否应将消息发送给客户端,状态> = 500时为false
err.expose = status < 500
err.status = err.statusCode = status
}
// 将自定义的业务属性props挂载到err
for (var key in props) {
// 排除黑名单属性
if (key !== 'status' && key !== 'statusCode') {
err[key] = props[key]
}
}
return err
}
/**
* Create HTTP error abstract base class.
* 模拟一个HTTP抽象基类,作为其他派生类的基类
* @private
*/
function createHttpErrorConstructor () {
// 抽象类、不可以直接被实例化
function HttpError () {
throw new TypeError('cannot construct abstract class')
}
// 继承Error
inherits(HttpError, Error)
return HttpError
}
/**
* 根据HttpError基类,生成一个客户端异常构造器4xx。
* @private
*/
function createClientErrorConstructor (HttpError, name, code) {
// 拼接className
var className = toClassName(name)
function ClientError (message) {
// 获取error message
var msg = message != null ? message : statuses[code]
// 生成Error
var err = new Error(msg)
// capture a stack trace to the construction point 在err上生成.stack错误堆栈属性
// 具体看API http://nodejs.cn/api/errors.html#errors_error_capturestacktrace_targetobject_constructoropt
Error.captureStackTrace(err, ClientError)
// adjust the [[Prototype]] 设置原型
setPrototypeOf(err, ClientError.prototype)
// 重新定义error message
Object.defineProperty(err, 'message', {
enumerable: true,
configurable: true,
value: msg,
writable: true
})
// 重新定义 error name
Object.defineProperty(err, 'name', {
enumerable: false,
configurable: true,
value: className,
writable: true
})
return err
}
inherits(ClientError, HttpError)
// 由于大家都是公用ClientError函数、基于inherits方式实现继承,因此需要自定义自身的Func.name
nameFunc(ClientError, className)
ClientError.prototype.status = code
ClientError.prototype.statusCode = code
ClientError.prototype.expose = true
return ClientError
}
/**
* Create function to test is a value is a HttpError.
* 判断val是否是一个HttpError
* 高阶函数
* @private
*/
function createIsHttpErrorFunction (HttpError) {
return function isHttpError (val) {
if (!val || typeof val !== 'object') {
return false
}
// 鸭子类型 duck type
if (val instanceof HttpError) {
return true
}
// 满足自定义的error写法
return val instanceof Error &&
typeof val.expose === 'boolean' &&
typeof val.statusCode === 'number' && val.status === val.statusCode
}
}
/**
* 根据HttpError基类,生成一个服务端异常构造器5xx。
* @private
*/
function createServerErrorConstructor (HttpError, name, code) {
var className = toClassName(name)
function ServerError (message) {
// create the error object
var msg = message != null ? message : statuses[code]
var err = new Error(msg)
// capture a stack trace to the construction point
Error.captureStackTrace(err, ServerError)
// adjust the [[Prototype]]
setPrototypeOf(err, ServerError.prototype)
// redefine the error message
Object.defineProperty(err, 'message', {
enumerable: true,
configurable: true,
value: msg,
writable: true
})
// redefine the error name
Object.defineProperty(err, 'name', {
enumerable: false,
configurable: true,
value: className,
writable: true
})
return err
}
inherits(ServerError, HttpError)
nameFunc(ServerError, className)
ServerError.prototype.status = code
ServerError.prototype.statusCode = code
ServerError.prototype.expose = false
return ServerError
}
/**
* 如果可以的话、设置Function.name
* @private
*/
function nameFunc (func, name) {
var desc = Object.getOwnPropertyDescriptor(func, 'name')
if (desc && desc.configurable) {
// 可修改、Object.defineProperty
desc.value = name
Object.defineProperty(func, 'name', desc)
}
}
/**
* Populate the exports object with constructors for every error class.
* 根据code构建对应异常类构造函数,并且默认导出、用于外部使用
* @private
*/
function populateConstructorExports (exports, codes, HttpError) {
// 遍历codes
codes.forEach(function forEachCode (code) {
var CodeError
// statuses[code]: 根据给定的状态码返回对应的http status message,比如205 => Reset Content
// toIdentifier: 转换字符串为合法的标识符 比如 Reset Content => ResetContent
var name = toIdentifier(statuses[code])
switch (codeClass(code)) {
// 处理4xx、5xx,根据name、code生成不同的客户端异常、服务端异常的构造函数
case 400:
CodeError = createClientErrorConstructor(HttpError, name, code)
break
case 500:
CodeError = createServerErrorConstructor(HttpError, name, code)
break
}
if (CodeError) {
// export the constructor
// 暴露构造函数、
// var createError = require('./source')
// var PreconditionFailed = createError['412'] // 可以通过[code]加载对应异常构造函数
// console.log(new PreconditionFailed('xyz'))
// var NotExtendedError = createError.NotExtended // 可以通过[name]加载对应异常构造函数
// console.log(new NotExtendedError('NotExtendedError'))
exports[code] = CodeError
exports[name] = CodeError
}
})
// backwards-compatibility
exports["I'mateapot"] = deprecate.function(exports.ImATeapot,
'"I\'mateapot"; use "ImATeapot" instead')
}
/**
* Get a class name from a name identifier.
* @private
*/
function toClassName (name) {
// 兼容处理InternalServerError类似以Error结尾的name
return name.substr(-5) !== 'Error'
? name + 'Error'
: name
}
代码还是很有意思,可以看看。
好了,今天就到这结束了,下期见。
ps:如果你对node也有兴趣,欢迎关注我公众号:xyz编程日记。