Koa源码阅读

查看Koa,version@2.7源码,总共只有四个文件application.jscontext.jsrequest.jsresponse.js;分别对应Koa应用入口、上下文环境、请求对象和响应对象

一、application.js

1、总览

可以看到这个模块以Node内部模块events为父类,导出一个创建Koa应用的构造函数,这个构造函数中有几个属性值,我们主要看

2、属性

  • middleware,保存中间件的数组;
  • context,上下文对象;
  • request,请求对象;
  • response,响应对象;

3、方法

3.1 listen

我们使用中一般只传入一个number,让服务启动在某个端口号。可以看到listen内部调用的是node创建httpserver的方法。这里主要看http.createServer方法的接受一个请求的监听函数,同时这个函数的两个参数分别为req请求对象和res响应对象;
查看callback方法,

callback() {
    const fn = compose(this.middleware);

    if (!this.listenerCount('error')) this.on('error', this.onerror);

    const handleRequest = (req, res) => {
      const ctx = this.createContext(req, res);
      return this.handleRequest(ctx, fn);
    };

    return handleRequest;
  }

开始通过compose返回链式处理中间件的函数,这里查看componse方法是如何封装的。首先先校验了中间件数组是否符合要求,然后在返回的函数中可以看到,通过promise.resolve将返回中间件的执行结果,中间件第一个参数是ctx,第二个参数是next,这里可以看到dispatch就是对应next,当我们执行该方法就会将中间件继续递归进行调用。这样就形成Koa中那种洋葱模型的执行顺序的一个promise。

//componse方法
function compose (middleware) {
  if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
  for (const fn of middleware) {
    if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
  }

  /**
   * @param {Object} context
   * @return {Promise}
   * @api public
   */

  return function (context, next) {
    // last called middleware #
    let index = -1
    return dispatch(0)
    function dispatch (i) {
      if (i <= index) return Promise.reject(new Error('next() called multiple times'))
      index = i
      let fn = middleware[i]
      if (i === middleware.length) fn = next
      if (!fn) return Promise.resolve()
      try {
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}

然后在看handleRequest,这里开始有点迷惑到我,他在callback里面定义了一个函数也叫handleRequest,但后面这个只是一个函数变量的名字,里面的handleRequest才是我们要分析的。为了方便说明查看,可以临时改写成以下这种写法。

callback() {
    const fn = compose(this.middleware);

    if (!this.listenerCount('error')) this.on('error', this.onerror);

    const xxx = (req, res) => {
      const ctx = this.createContext(req, res);
      return this.handleRequest(ctx, fn);
    };

    return xxx;
  }

在xxx函数中,首先使用createContext方法,将请求报文、响应报文等对象挂载在上下文对象上, 其中request是Koa请求对象,response是Koa响应对象,app为当前Koa服务实例对象,req为Node的请求对象,res为Node响应对象。然后执行handleRequest方法,参数为上下文和处理中间件的函数。最后返回一次请求的执行结果。这里可以看到返回状态码默认为404

createContext(req, res) {
  const context = Object.create(this.context);
  const request = context.request = Object.create(this.request);
  const response = context.response = Object.create(this.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;
}
handleRequest(ctx, fnMiddleware) {
  const res = ctx.res;
  res.statusCode = 404;
  const onerror = err => ctx.onerror(err);
  const handleResponse = () => respond(ctx);
  onFinished(res, onerror);
  return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}
3.2 handleRequest,createContext

如上callback中使用的两个方法

3.3 use

use方法很简单,就是将单个中间件push进数组保存

3.3 toJSON

toJSON方法引入一个only的包,打开可以看到就是将一个对象过滤返回出一个新的只包含指定属性值的对象

3.4 onerror

打印错误日志

3.5 respond

处理响应结果对象,涉及了一些buffer和stream的概念,最后返回响应结果

二、context.js

返回就是上下文环境的对象原型

1、inspect

返回当前对象

2、toJSON

返回请求报文和响应报文和其他一些属性的对象

3、assert,onerror

异常处理

4、get cookies,set cookies

cookies取值和赋值的函数

5、delegate

从上面看到当前上下文原型的属性、方法还很少,后面主要是通过delegate定义其他属性方法。查看delegate依赖包

function Delegator(proto, target) {
  if (!(this instanceof Delegator)) return new Delegator(proto, target);
  this.proto = proto;
  this.target = target;
  this.methods = [];
  this.getters = [];
  this.setters = [];
  this.fluents = [];
}

返回一个以Delegator为构造函数的对象,返回的对象定义了上面几个属性
proto参数就是传入的上下文原型对象,target因为这个Delegator函数调用了两次分别是responserequest
然后Delegator原型上有几个方法methodaccessgettersetterfluent,调用以后都把原对象返回了,所以在context.js中使用时可以链式调用。以method方法为例子:

Delegator.prototype.method = function(name){
  var proto = this.proto;
  var target = this.target;
  this.methods.push(name);

  proto[name] = function(){
    return this[target][name].apply(this[target], arguments);
  };

  return this;
};

调用该方法,首先将方法名保存进方法集合数组,然后在原对象上定义属性方法,这时的proto就是上下文原型对象,然后target参数传递表示该方法具体是用的response还是request对象上的方法。
总结就是上下文原型对象就是定义了responserequest等这些重要属性,同时将这两个对象中的方法,快捷的直接绑定在原型对象上,所以有些属性或方法使用时可以省略response和request。

三、request.js

定义一个request对象,将Node请求报文req的一些属性和方法挂载该对象上并返回

1、header、headers都表示请求头

2、url等等

3、就是将一些Node中的请求对象里的属性挂载在Koa的请求对象request上

四、response.js

和request类似,返回一个response对象,再回顾下application.js中的createContext方法;

其中request是Koa请求对象,response是Koa响应对象,app为当前Koa服务实例对象,req为Node的请求对象,res为Node响应对象

其他还包括一些响应结果返回、重定向等方法,具体没有去细看

Image placeholder
maoxfjob
未设置
  80人点赞

没有讨论,发表一下自己的看法吧

推荐文章
学习 nodejs+mongodb+koa2 写接口(二) koa2教程入门

一.hellokoa安装koa2#初始化package.json npminit #安装koa2 npminstallkoahelloworld代码constKoa=require('koa') c

Requests 源码阅读-Day1

Requests源码阅读-Day1[toc]Requests介绍requests是一个Python的网络请求库,和urllib、httplib之流相比起来最大的优点就是好用,requests官方标榜的

Java 与 Kotlin 系列文章 (一):性能问题

随着对Kotlin越来越深入的了解,我发现市面上关于Kotlin方面,比较深入的资料几乎是0,所以我决定,将Kotlin各个方面的研究作为我的研究生课题,而性能问题往往是程序员最佳关注的内容,所以第

采用 PHP-quickorm/Captcha,用最快的速度在 PHP 语言下实现验证码功能

要调用起这个库,门槛十分低,但是建议满足以下几个条件: PHP5+ PHPGD扩展 Composer(非必须) 安装方法 首先我们花30秒来引入一下这个库,主要有以下两种方式。 其一、使用Comp

集成 think-ORM 的 symfony bundle thinkorm-bundle

thinkorm-bundleSymfonyThinkOrmBundle关于thinkorm-bundle允许在你symfony使用thinkorm.所安装$composerrequireccwwwo

使用 ES6 写 Koa Web 项目

完整代码:传送门我们node.js只是实现了部分ES6的语法,所以为了让我们ES6的代码能100%在node.js下执行,必须使用我们的babel把ES6代码编译成nodejs可执行的代码。node环

资源混淆是如何影响到Kotlin协程的

导言随着kotlin的使用,协程也慢慢在我们工程中被开始被使用起来,但在我们工程中却遇到了一个问题,经过资源混淆处理之后的apk包,协程却不如期工作。那么两者到底有什么关联呢,资源混淆又是如何影响到协

实现koa核心代码

导语:实现简单的koa核心代码,便于理解koa原理。顺便学习koa代码的那些骚操作。简单分析koa创建一个http服务,只绑一个中间件。创建index.js/**index.js*/ constKoa

koala如何压缩css?

koala如何压缩css?koala是一个前端预处理器语言图形编译工具,支持Less、Sass、Compass、CoffeeScript,帮助web开发者更高效地使用它们进行开发。跨平台运行,完美兼容

Kotlin如何安全访问lateinit变量

Kotlin设计之初就是不允许非null变量在声明期间不进行初始化的,为了解决这个问题,Kotlinlateinit允许我们先声明一个变量,然后在程序执行周期的将来某个时候将其初始化,让编译检查时不会

学习 nodejs+mongodb+koa2 写接口(一) 环境布置

一.环境准备最近在学用Nodejs写后端接口,了解到koa2是Nodejs的一个框架。可以快速开发后端接口,同时也能更快熟悉Nodejs以下是所需的环境node  v7.6+,可以用nvm或者n安装指

为了面试后不再回去等消息,我决定阅读源码

01、读源码的经历刚参加工作那会,没想过去读源码,更没想过去改框架的源码;总想着别人的框架应该是完美的、万能的,应该不需要改。另外即使我改了源码,怎么样让我的改动生效了?项目中引用的不还是没改的jar

教你阅读 Python 开源项目代码

为什么要阅读开源代码 阅读Python开源项目代码主要有如下三个原因: 在工作过程中遇到一些问题Google和StackOverFlow等网站找不到解决办法,只能去翻源码。 对某些项目或者方向非常感

交易所源码出售

分享链接:https://www.v2ex.com/t/624890github地址指数型差价合约交易所系统、ICFD指数型差价合约交易所、BTC比特币杠杠交易、领先数字货币杠杆交易所(高达100倍杠

HDFS 源码解读:HadoopRPC 实现细节的探究

桔妹导读:HDSF作为分布式文件系统,常常涉及DataNode、NameNode、Client之间的配合、相互调用才能完成完整的流程。为了降低节点之间的耦合性,HDFS将节点间的调用抽象成不同的接口,

单点登录系统原理与实现,图文并茂,附源码

本原文:整理自互联网一、单系统登录机制1、http无状态协议web应用采用browser/server架构,http作为通信协议。http是无状态协议,浏览器的每一次请求,服务器会独立处理,不与之前或

GitHub遭黑客攻击:窃取数百源码并勒索比特币

大数据文摘编辑部出品五一过后,一些程序员查看自己托管到GitHub上的代码时发现,他们的源代码和Repo都已消失不见,上周四,一位Reddit用户写了一篇帖子,说他的存储库被黑了。代码也被删除了,取而

JVM CPU Profiler技术原理及源码深度解析

本文介绍了JVM平台上CPUProfiler的实现原理,希望能帮助读者在使用类似工具的同时也能清楚其内部的技术实现。引言研发人员在遇到线上报警或需要优化系统性能时,常常需要分析程序运行行为和性能瓶颈。

B站工程源码泄露,Github标星9k+,内含部分用户名密码

大数据文摘出品作者:蒋宝尚、宋欣仪昨儿个,文摘菌日常在B站上看看本山大爷的视频,听听吴亦凡的大碗面。突然弹幕画风突变,评论区集体喊话B站,“你家后院着火了”。原来,Bilibili的网站后台源码被发到

【源码解析】扒开ArrayList的外衣

积千里跬步,汇万里江河;每天进步一点点,终有一天将成大佬。本文内容当然ArrayList里的方法不止这些,本文主要讲一些常用的方法方法变量Arraylist里的方法变量主要有以下几个1.构造方法1.1

Requests源码分析

Requests源码分析最近python学习到了瓶颈了,这次准备从KennethReitz大神的requests入手分析源码,看大神的代码是一种学习的好方法,让我从中学到很多以前不知道的知识reque

BAT大牛推荐开发人员必备Spring源码剖析文档,深度剖析Spring

为什么学习读源码我们每天都和代码打交道。经过数年的基础教育和职业培训,大部分程序员都会「写」代码,或者至少会抄代码和改代码。但是,会读代码的并不在多数,会读代码又真正读懂一些大项目的源码的,少之又少。

workerman源码-workerman启动流程

前面我们跟着代码看了一遍workerman的初始化流程.但对于如何监听端口.等操作还没有具体的实现.我们这次就来看一下.workerman是如何监听端口并运行的.runAll在前面我们初始化方法过后,

vue源码解读(四)Vue中的异步更新策略

欢迎star我的github仓库,共同学习~目前vue源码学习系列已经更新了6篇啦~https://github.com/yisha0307/...快速跳转: Vue的双向绑定原理(已完成) 说说vu

源码分析 | 咋嘞?你的IDEA过期了吧!加个Jar包就破解了,为什么?

微信公众号:bugstack虫洞栈|博客:https://bugstack.cn沉淀、分享、成长,专注于原创专题案例,以最易学习编程的方式分享知识,让自己和他人都能有所收获。目前已完成的专题有;Net