hey 老伙计
今天我们来讲一下koa的context.js还有一些tj自己写的库吧。
context.js
'use strict';
const createError = require('http-errors');
const httpAssert = require('http-assert');
const delegate = require('delegates');
const statuses = require('statuses');
const proto = module.exports = {
//前面文章有讲过这个了,这里不描述了
inspect() {
if (this === proto) return this;
return this.toJSON();
},
toJSON() {
return {
request: this.request.toJSON(),//实际上调用request.js的toJSON(下同)
response: this.response.toJSON(),
app: this.app.toJSON(),
originalUrl: this.originalUrl,
req: '<original node req>',
res: '<original node res>',
socket: '<original node socket>'
};
},
/**
* Similar to .throw(), adds assertion.
*
* this.assert(this.user, 401, 'Please login!');
*
* See: https://github.com/jshttp/http-assert
*
* @param {Mixed} test
* @param {Number} status
* @param {String} message
* @api public
*/
assert: httpAssert,
/**
* Throw an error with `msg` and optional `status`
* defaulting to 500. Note that these are user-level
* errors, and the message may be exposed to the client.
*
* this.throw(403)
* this.throw('name required', 400)
* this.throw(400, 'name required')
* this.throw('something exploded')
* this.throw(new Error('invalid'), 400);
* this.throw(400, new Error('invalid'));
*
* See: https://github.com/jshttp/http-errors
*
* @param {String|Number|Error} err, msg or status
* @param {String|Number|Error} [err, msg or status]
* @param {Object} [props]
* @api public
*/
//throw方法。上面是使用的方法。我们 常用来在中间件throw发出一些错误状态码。
//从而使得上级中间件可以try catch这个错误从而响应
//createError([status], [message], [properties])
//properties - custom properties to attach to the object
throw(...args) {
throw createError(...args);
},
//默认的一个错误处理
onerror(err) {
// don't do anything if there is no error.
// this allows you to pass `this.onerror`
// to node-style callbacks.
if (null == err) return;
// 如果error不是Error实例。此时生成一个错误实例给下文处理
if (!(err instanceof Error)) err = new Error(`non-error thrown: ${err}`);
let headerSent = false;
//当然需要可写且没有被发送
if (this.headerSent || !this.writable) {
headerSent = err.headerSent = true;
}
//触发事件
this.app.emit('error', err, this);
//发送了肯定啥都不能干了
if (headerSent) {
return;
}
const { res } = this;
//解构一下获得response
//兼容咯
//首次清除所有的headers
if (typeof res.getHeaderNames === 'function') {
res.getHeaderNames().forEach(name => res.removeHeader(name));
} else {
res._headers = {}; // Node < 7.7
}
// 然后设置为错误的headers标识
this.set(err.headers);
//强制text/plain
this.type = 'text';
// 支持ENOENT
if ('ENOENT' == err.code) err.status = 404;
// 默认转换成500状态码
if ('number' != typeof err.status || !statuses[err.status]) err.status = 500;
//响应
const code = statuses[err.status];
const msg = err.expose ? err.message : code;
this.status = err.status;
this.length = Buffer.byteLength(msg);
this.res.end(msg);
//跟原生的一样嘛。
//给我们一个提示。我们要使一个连接关闭。那么ctx.res.end(msg);
}
};
/**
* Response delegation.
*/
//委托到这个上下文对象里
//委托方法 与属性的getter或者setter
delegate(proto, 'response')
.method('attachment')
.method('redirect')
.method('remove')
.method('vary')
.method('set')
.method('append')
.method('flushHeaders')
.access('status')
.access('message')
.access('body')
.access('length')
.access('type')
.access('lastModified')
.access('etag')
.getter('headerSent')
.getter('writable');
/**
* Request delegation.
*/
delegate(proto, 'request')
.method('acceptsLanguages')
.method('acceptsEncodings')
.method('acceptsCharsets')
.method('accepts')
.method('get')
.method('is')
.access('querystring')
.access('idempotent')
.access('socket')
.access('search')
.access('method')
.access('query')
.access('path')
.access('url')
.getter('origin')
.getter('href')
.getter('subdomains')
.getter('protocol')
.getter('host')
.getter('hostname')
.getter('URL')
.getter('header')
.getter('headers')
.getter('secure')
.getter('stale')
.getter('fresh')
.getter('ips')
.getter('ip');
因为下一个篇幅准备将最重要的application.js
所以这个接下来准备说几个引入的库源码
koa convert
用于什么?用于将koa1的中间件转化为promise
看到co就应该有这么个想法了= =
'use strict'
const co = require('co')
//引入co
const compose = require('koa-compose')
module.exports = convert
function convert (mw) {
//进行判断
if (typeof mw !== 'function') {
throw new TypeError('middleware must be a function')
}
if (mw.constructor.name !== 'GeneratorFunction') {
// assume it's Promise-based middleware
return mw
}
const converted = function (ctx, next) {
return co.call(ctx, mw.call(ctx, createGenerator(next)))
}
converted._name = mw._name || mw.name
return converted
}
function * createGenerator (next) {
return yield next()
}
// convert.compose(mw, mw, mw)
// convert.compose([mw, mw, mw])
// koa-compose 日后再说嘻嘻^v^
convert.compose = function (arr) {
if (!Array.isArray(arr)) {
arr = Array.from(arguments)
}
return compose(arr.map(convert))
}
//我的天啊。这个疯子还支持回退
//回退方法很是精妙啊
convert.back = function (mw) {
if (typeof mw !== 'function') {
throw new TypeError('middleware must be a function')
}
if (mw.constructor.name === 'GeneratorFunction') {
// assume it's generator middleware
return mw
}
const converted = function * (next) {
let ctx = this
let called = false
// no need try...catch here, it's ok even `mw()` throw exception
yield Promise.resolve(mw(ctx, function () {
//使得next仅仅被调用一次
if (called) {
// guard against multiple next() calls
// https://github.com/koajs/compose/blob/4e3e96baf58b817d71bd44a8c0d78bb42623aa95/index.js#L36
return Promise.reject(new Error('next() called multiple times'))
}
called = true
return co.call(ctx, next)
}))
}
converted._name = mw._name || mw.name
return converted
}
is-generator-function
'use strict';
//减少查找引用,常见的优化方法
var toStr = Object.prototype.toString;
var fnToStr = Function.prototype.toString;
//这个正则匹配function *但是好像有点bug
//* function(){}也会受到判定true
var isFnRegex = /^\s*(?:function)?\*/;
var hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
var getProto = Object.getPrototypeOf;
var getGeneratorFunc = function () { // eslint-disable-line consistent-return
if (!hasToStringTag) {
return false;
}
try {
return Function('return function*() {}')();
} catch (e) {
}
};
var generatorFunc = getGeneratorFunc();
var GeneratorFunction = generatorFunc ? getProto(generatorFunc) : {};
//主要从三点看。
//一点是function toString
//一点是[object GeneratorFunction] Object toString
//一点是从原型看(内部[[Prototype]]属性的值)
module.exports = function isGeneratorFunction(fn) {
if (typeof fn !== 'function') {
return false;
}
if (isFnRegex.test(fnToStr.call(fn))) {
return true;
}
if (!hasToStringTag) {
var str = toStr.call(fn);
return str === '[object GeneratorFunction]';
}
return getProto(fn) === GeneratorFunction;
};
这次写的好粗糙啊= =
希望在结尾部分能写好。
sry sry sry
看完全文都是好样的!!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。