返回

Koa 源码剖析,深入了解 Koa 原理

前端

Koa 源码剖析

首先要说明的是,本文分析的 Koa 源码版本是 Koa 的第一个发布版本(0.0.2)。该版本的文件结构非常简单,只有三个文件:application.js、context.js 和 status.js。接下来,我们将依次分析这三个文件。

Context

Context 是 Koa 中最重要的部分之一,也是代码量最多的部分。

// context.js
var util = require('util');
var Stream = require('stream');
var Response = require('./response');

function Context() {
  this.state = {};
  this.res = new Response();
}

util.inherits(Context, Stream);

Context.prototype.inspect = function() {
  return util.format('%s %s (%d %s)', this.method, this.originalUrl, this.res.statusCode, http.STATUS_CODES[this.res.statusCode]);
};

exports = module.exports = Context;

Context 是一个继承自 Stream 类的对象,它包含了请求和响应相关的属性和方法。

  • this.state:一个存储状态信息的对象,可以被中间件和控制器使用。
  • this.res:一个 Response 对象,用于发送响应。
  • this.method:请求方法,如 GETPOST
  • this.originalUrl:原始请求 URL,包括查询参数。
  • this.res.statusCode:响应状态码,如 200404

Application

Application 是 Koa 的主类,它负责处理请求并将其路由到相应的中间件和控制器。

// application.js
var http = require('http');
var EventEmitter = require('events').EventEmitter;
var slice = Array.prototype.slice;
var parse = require('url').parse;
var Context = require('./context');
var Response = require('./response');

function Application() {
  this.middleware = [];
}

util.inherits(Application, EventEmitter);

Application.prototype.listen = function() {
  var server = http.createServer(this.callback());
  return server.listen.apply(server, arguments);
};

Application.prototype.callback = function() {
  var fn = this.handle.bind(this);
  return function(req, res) {
    var ctx = createCtx(req, res);
    fn(ctx).then(function() {
      res.end();
    }).catch(function(err) {
      ctx.app.emit('error', err, ctx);
      res.end();
    });
  };
};

Application.prototype.use = function(fn) {
  if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
  this.middleware.push(fn);
  return this;
};

Application.prototype.handle = function(ctx, next) {
  var idx = -1;
  var stack = this.middleware;
  var next = function() {
    var layer = stack[++idx];
    if (!layer) {
      // delegate to the final handler (which is the app itself)
      return this.respond(ctx);
    }
    try {
      // convert to Promise
      var onerror = handleError(ctx, next);
      var res = Promise.resolve(layer(ctx, onerror));
      // capture rejection for Koa's downstream error handling middleware
      // or any other rejections that happen downstream
      if (res && typeof res.catch === 'function') {
        return res.catch(onerror);
      }
    } catch (err) {
      // if an error happens in a synchronous middleware,
      // pass it to the downstream error middleware
      return onerror(err);
    }
  };
  // invoke stack of middlewares
  next();
};

Application.prototype.respond = function(ctx) {
  // allow bypassing middleware
  if (ctx.res._explicitStatus) {
    ctx.res.end();
    return;
  }
  // attempt to match request path
  var path = parse(ctx.req.url).pathname;
  var layer = match(this.router, path);
  if (layer) {
    var fn = layer.handler;
    var arity = fn.length;
    var onerror = handleError(ctx, ctx.onerror);
    if (arity < 4) {
      return fn(ctx, onerror);
    } else {
      // request, response, next(err)
      return fn(ctx.req, ctx.res, onerror);
    }
  }
};

exports = module.exports = Application;

Application 对象主要包含以下方法:

  • this.listen():监听指定端口并启动服务器。
  • this.callback():返回一个 Koa 中间件函数,该函数用于处理请求。
  • this.use():添加中间件。
  • this.handle():处理请求。
  • this.respond():响应请求。

Status

Status 对象包含了一系列 HTTP 状态码,方便在应用程序中使用。

// status.js
module.exports = {
  200: 'OK',
  201: 'Created',
  202: 'Accepted',
  204: 'No Content',
  301: 'Moved Permanently',
  302: 'Found',
  304: 'Not Modified',
  400: 'Bad Request',
  401: 'Unauthorized',
  403: 'Forbidden',
  404: 'Not Found',
  500: 'Internal Server Error'
};

总结

通过对 Koa 源码的分析,我们了解了 Koa 的基本原理和实现机制。这有助于我们更好地理解和使用 Koa,并开发出更加强大的 Node.js 应用。