koa2 源码解读 application

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;

猜你喜欢

转载自www.cnblogs.com/itstone/p/11765795.html