koa2的源码比较简单,重点解读aplication,
其中context源码比较简单,主要是一些error cookies等,重点可以关注下delegate,delegate模块中,主要通过prototype的方式进行属性的增加。
request和response两个模块都是get set的一些基础api及封装的node原始方法
application
'use strict'; //严格模式
/**
* Module dependencies.
*/
const isGeneratorFunction = require('is-generator-function');
const debug = require('debug')('koa:application');
const onFinished = require('on-finished');
const response = require('./response');
const compose = require('koa-compose');
const isJSON = require('koa-is-json');
const context = require('./context');
const request = require('./request');
const statuses = require('statuses');
const Emitter = require('events');
const util = require('util');
const Stream = require('stream');
const http = require('http');
const only = require('only');
const convert = require('koa-convert');
const deprecate = require('depd')('koa');
const { HttpError } = require('http-errors');
/**
* constructor() 构造函数
* listen() 调用原生http模块创建服务并监听
* use() 中间件处理
* callback() http请求的回调函数
* handleRequest() 请求真正的回调函数
* createContext() 创建上下文对象
* respond() 所有中间件处理完后自动响应
* onerror() 处理错误信息
*
*/
/**
* Expose `Application` class.
* Inherits from `Emitter.prototype`.
*/
module.exports = class Application extends Emitter {
/**
* Initialize a new `Application`.
*
* @api public
*/
/**
*
* @param {object} [options] Application options
* @param {string} [options.env='development'] Environment
* @param {string[]} [options.keys] Signed cookie keys
* @param {boolean} [options.proxy] Trust proxy headers
* @param {number} [options.subdomainOffset] Subdomain offset
*
*/
constructor(options) {
super();
options = options || {};
this.proxy = options.proxy || false; //是否允许跨域
this.subdomainOffset = options.subdomainOffset || 2; // 子域名允许请求几级连接
this.env = options.env || process.env.NODE_ENV || 'development'; //node的执行环境
if (options.keys) this.keys = options.keys;
this.middleware = []; //所有的中间件的存入
this.context = Object.create(context); //每次实例化都重新赋值,为保证多次实例化时保持不冲突,和单例模式成反例
this.request = Object.create(request);
this.response = Object.create(response);
if (util.inspect.custom) {
this[util.inspect.custom] = this.inspect; //保存88行代码中的内容
}
}
/**
* Shorthand for:
*
* http.createServer(app.callback()).listen(...)
*
* @param {Mixed} ...
* @return {Server}
* @api public
*/
listen(...args) {
debug('listen');
const server = http.createServer(this.callback()); //原生http模块创建服务并监听
return server.listen(...args);
}
/**
* Return JSON representation.
* We only bother showing settings.
*
* @return {Object}
* @api public
*/
toJSON() {
return only(this, [ //only 对传入的数据使用reduce进行重组
'subdomainOffset',
'proxy',
'env'
]);
}
/**
* Inspect implementation.
*
* @return {Object}
* @api public
*/
inspect() {
return this.toJSON(); //数据重组
}
/**
* Use the given middleware `fn`.
*
* Old-style middleware will be converted.
*
* @param {Function} fn
* @return {Application} self
* @api public
*/
use(fn) {
if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');//必须是一个function
if (isGeneratorFunction(fn)) {
deprecate('Support for generators will be removed in v3. ' +
'See the documentation for examples of how to convert old middleware ' +
'https://github.com/koajs/koa/blob/master/docs/migration.md');
fn = convert(fn);
}
debug('use %s', fn._name || fn.name || '-'); //DEBUG=koa* node --harmony app.js 调试时输出中间件调用及时长
this.middleware.push(fn); //将中间件加入到middleware数组中
return this;
}
/**
* Return a request handler callback
* for node's native http server.
*
* @return {Function}
* @api public
*/
callback() {
const fn = compose(this.middleware); //将这些中间件组合后拿到执行链函数fn
if (!this.listenerCount('error')) this.on('error', this.onerror); //如果没有监听则报错
const handleRequest = (req, res) => { //事件处理函数
const ctx = this.createContext(req, res); //创建一个ctx
return this.handleRequest(ctx, fn); //交给157行的handleRequest
};
return handleRequest;
}
/**
* Handle request in callback.
*
* @api private
*/
handleRequest(ctx, fnMiddleware) {
const res = ctx.res;
res.statusCode = 404; //初始赋值
const onerror = err => ctx.onerror(err);
const handleResponse = () => respond(ctx); //211行详解
onFinished(res, onerror);
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}
/**
* Initialize a new context.
*
* @api private
*/
createContext(req, res) {
const context = Object.create(this.context);//通过context对象的原型创建
const request = context.request = Object.create(this.request);//通过request对象的原型创建,this.request指的是原生的request,修改this.request中的属性就是修改原生的对应的属性数据
const response = context.response = Object.create(this.response);//通过response对象的原型创建
context.app = request.app = response.app = this; //传递
context.req = request.req = response.req = req;
context.res = request.res = response.res = res;
request.ctx = response.ctx = context;
request.response = response; //交叉传递
response.request = request;
context.originalUrl = request.originalUrl = req.url;
context.state = {};
return context;
}
/**
* Default error handler.
*
* @param {Error} err
* @api private
*/
onerror(err) {
if (!(err instanceof Error)) throw new TypeError(util.format('non-error thrown: %j', err)); //检测err不是Error实例时,创建一个Error的实例
if (404 == err.status || err.expose) return;
if (this.silent) return;
const msg = err.stack || err.toString(); //将err堆栈的信息拿出来
console.error(); //控制台打印Error信息
console.error(msg.replace(/^/gm, ' '));
console.error();
}
};
/**
* Response helper.
*/
function respond(ctx) {
// allow bypassing koa
if (false === ctx.respond) return; //允许绕过KOA 为写入原始的res对象而不是让koa处理你的rsponse
if (!ctx.writable) return;
const res = ctx.res;
let body = ctx.body; //外面Middleware传入的body数据
const code = ctx.status; //当前状态码
// ignore body
if (statuses.empty[code]) { //当前状态码是空的,则清掉body并结束
// strip headers
ctx.body = null;
return res.end();
}
if ('HEAD' == ctx.method) { //head部分
if (!res.headersSent && isJSON(body)) { //判断当前header没有被发送并且是 重组后的json数据
ctx.length = Buffer.byteLength(JSON.stringify(body)); //则重新序列化 取长度
}
return res.end();
}
// status body
if (null == body) { // body部分 不为null
if (ctx.req.httpVersionMajor >= 2) { //根据http major的版本 分别对body进行初始化
body = String(code);
} else {
body = ctx.message || String(code);
}
if (!res.headersSent) {
ctx.type = 'text';
ctx.length = Buffer.byteLength(body);
}
return res.end(body);
}
//以下为支持各种格式的body
// responses
if (Buffer.isBuffer(body)) return res.end(body);
if ('string' == typeof body) return res.end(body);
if (body instanceof Stream) return body.pipe(res);
// body: json
body = JSON.stringify(body);
if (!res.headersSent) {
ctx.length = Buffer.byteLength(body);
}
res.end(body);
}
/**
* Make HttpError available to consumers of the library so that consumers don't
* have a direct dependency upon `http-errors`
*/
module.exports.HttpError = HttpError;