最近在复习node的基础知识,于是看了看koa2的源码,写此文分享一下包括了Koa2的使用、中间件及上下文对象的大致实现原理。

koa的github地址:https://github.com/koajs/koa.git

Koa2的安装和简单使用

需要 nodev7.6.0 或者更高的版本,为了支持 ES2015 and async

安装
npm install koa
Hello koa
const Koa = require('koa');
const app = new Koa(); // response
app.use(ctx => {
ctx.body = 'Hello Koa';
}); app.listen(3000);
中文的api文档:https://github.com/guo-yu/koa-guide

简单分析koa的代码

打开koa的源码,核心文件共四个在lib目录下,application.js,context.js,request.js,response.js

application.js

app的入口文件,就是一个构造函数

 简洁的代码
module.exports = class Application extends Emitter {
constructor() {
super();
//定义下面的属性
this.proxy = false;
this.middleware = [];
this.subdomainOffset = 2;
this.env = process.env.NODE_ENV || 'development';
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
}
//listen端口方法
listen(...args) {
debug('listen');
const server = http.createServer(this.callback());
return server.listen(...args);
} toJSON() {
return only(this, [
'subdomainOffset',
'proxy',
'env'
]);
} inspect() {
return this.toJSON();
} //中间件使用的use方法
use(fn) {
if (typeof fn !== 'function') throw new TypeError('middleware must be a 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 || '-');
this.middleware.push(fn);
return this;
} //上下文等关键代码
callback() {
const fn = compose(this.middleware); if (!this.listeners('error').length) this.on('error', this.onerror); const handleRequest = (req, res) => {
res.statusCode = 404;
const ctx = this.createContext(req, res);
const onerror = err => ctx.onerror(err);
const handleResponse = () => respond(ctx);
onFinished(res, onerror);
return fn(ctx).then(handleResponse).catch(onerror);
}; return handleRequest;
} //创建上下文
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.cookies = new Cookies(req, res, {
keys: this.keys,
secure: request.secure
});
request.ip = request.ips[0] || req.socket.remoteAddress || '';
context.accept = request.accept = accepts(req);
context.state = {};
return context;
} //处理报错
onerror(err) {
assert(err instanceof Error, `non-error thrown: ${err}`); if (404 == err.status || err.expose) return;
if (this.silent) return; const msg = err.stack || err.toString();
console.error();
console.error(msg.replace(/^/gm, ' '));
console.error();
}
};

开始的流程:

const app = new Koa();

然后通过 listen来启动服务:

const server = http.createServer(this.callback());
server.listen(...args);

看一下原生的启动方法:

// http server 例子
var server = http.createServer(function(serverReq, serverRes){
var url = serverReq.url;
serverRes.end( '您访问的地址是:' + url );
});
server.listen(3000);

对比发现this.callback()就是用来创建上下文和处理req和res的,接着看this.callback那个方法:

//处理中间件的使用,后面详细说明
const fn = compose(this.middleware); if (!this.listeners('error').length) this.on('error', this.onerror); const handleRequest = (req, res) => {
res.statusCode = 404;
// 创建上下文
const ctx = this.createContext(req, res);
const onerror = err => ctx.onerror(err); const handleResponse = () => respond(ctx);
onFinished(res, onerror);
//中间件返回promise对象,成功执行handleResponese,错误用onerror处理,
return fn(ctx).then(handleResponse).catch(onerror);
};
返回callback函数
return handleRequest;

启动服务:

server.listen(...args);

到此服务就起来了。在来看看中间件的使用原理:

use(fn) {
//判断做兼容处理
if (typeof fn !== 'function') throw new TypeError('middleware must be a 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 || '-');
//使用把中间件推送到middleware中保存
this.middleware.push(fn);
//返回this,为了连续调用
return this;
}

保存到this.middleware,在this.callback进程了处理:

const fn = compose(this.middleware);

看一下compose是怎么处理middleware,代码在const compose = require('koa-compose');

'use strict'

module.exports = compose

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!')
} 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, function next () {
//递归调用,直到全部中间件执行完
return dispatch(i + 1)
}))
} catch (err) {
//有错误
return Promise.reject(err)
}
}
}
}

通过上面巧妙的递归调用,执行完所有的中间件函数,返回继续启动流程,创建上下文,处理res,req等。

最新文章

  1. 关于imageOrientation
  2. sencha touch的开源插件和例子
  3. IT基础架构规划方案之实际网络设计案例
  4. interface
  5. C++命名空间&lt;转&gt;
  6. Map、Set、List、Queue、Stack的特点与用法
  7. DOJO 八 event dojo/on
  8. Spring 注解学习手札(七) 补遗——@ResponseBody,@RequestBody,@PathVariable (转)
  9. python分支
  10. CTF--web
  11. 【Codeforces 321E / BZOJ 5311】【DP凸优化】【单调队列】贞鱼
  12. 基础JAVA程序设计(多个类与方法的实现2)
  13. 关于信息系统设计与开发——案例:VIP系统
  14. Hbase的常见shell操作
  15. IO流-file
  16. 使用docker安装tomcat服务
  17. Atitit.每周计划日程表 流程表v3
  18. XML 增、删、改
  19. POJ1468 Sorting Slides
  20. 解决toad中number类型小数位数过长按科学计数法显示的问题

热门文章

  1. 洛谷 P2002 消息扩散
  2. python基础教程总结13——网络编程,
  3. 如何给SAP Cloud Connector Region列表中添加新的Region
  4. EF6.0注意事项
  5. UVA 11997 K Smallest Sums (多路归并)
  6. PAT (Basic Level) Practise (中文)- 1015. 德才论 (25)
  7. odoo前端
  8. vue中文本域限制字数的方法
  9. Template 基础篇-函数模板(待看
  10. Ubuntu Server 18.04 LTS安装