koa框架需要实现四个大模块,分别是:
- 封装node http server、创建Koa类构造函数
- 构造request、response、context对象
- 中间件机制和剥洋葱模型的实现
- 错误捕获和错误处理
运行脚本 app.js, 访问相应的端口, 控制台打印1,2,3,4,5
const Koa = require("koa");
const app = new Koa();
app.use(async (ctx, next) => {
console.log(1);
await next();
console.log(5);
});
app.use(async (ctx, next) => {
console.log(2);
await next();
console.log(4);
});
app.use(async (ctx) => {
console.log(3);
ctx.body = "Hello World";
});
app.listen(3000);
1. 封装node http server、创建Koa类构造函数
./src/application.js
const Emitter = require('events');
const http = require('http');
module.exports = class Application extends Emitter {
constructor(options) {
super();
this.callbackFn;
}
listen(...args) {
const server = http.createServer(this.callback());
return server.listen(...args);
}
use(fn) {
this.callbackFn = fn;
return this;
}
callback() {
return (req, res) => {
this.callbackFn(req, res);
}
}
}
运行脚本app.js
const Koa = require("./src/application.js");
const app = new Koa();
app.use((req, res) => {
// res.writeHead(200);
res.end('hello world');
});
app.listen(3000);
中间件机制和剥洋葱模型的实现
./src/application.js
const Emitter = require('events');
const http = require('http');
const compose = require('./compose.js')
module.exports = class Application extends Emitter {
constructor(options) {
super();
this.middleware = [];
this.compose = compose;
}
listen(...args) {
const server = http.createServer(this.callback());
return server.listen(...args);
}
use(fn) {
this.middleware.push(fn);
return this;
}
responseBody (ctx) {}
onError (err, ctx) {}
callback() {
return (req, res) => {
const ctx = "";
let respond = () => this.responseBody(ctx);
let onError = (err) => this.onError(err, ctx);
let fn = this.compose();
return fn(ctx).then(respond).catch(onError)
}
}
}
./src/compose.js
module.exports = compose
function compose (middleware) {
return async ctx => {
function createNext(middleware, oldNext) {
return async() => {
await middleware(ctx, oldNext);
}
}
let len = this.middleware.length;
let next = async() => {
return Promise.resolve();
}
for (let i = len - 1; i >= 0; i--) {
let currentMiddleware = this.middleware[i];
next = createNext(currentMiddleware, next);
}
await next();
}
}
运行脚本./app.js, 访问依次打印1,2,3, 4,5
构造request、response、context对象
./src/request.js
let url = require('url');
module.exports = {
get query() {
return url.parse(this.req.url, true).query;
}
}
./src/response.js
module.exports = {
get body () {
return this._body;
},
set body (data) {
this._body = data;
},
get status () {
return this.res.statusCode;
},
set status (statusCode) {
this.res.statusCode = statusCode;
}
}
./src/context.js
let proto = {};
function delegateSet(property, name) {
proto.__defineSetter__(name, function(val) {
this[property][name] = val;
})
}
function delegateGet (property, name) {
proto.__defineGetter__(name, function() {
return this[property][name];
})
}
let requestSet = [];
let requestGet = ['query'];
let responseSet = ['body', 'status'];
let responseGet = responseSet;
requestSet.forEach(ele => {
delegateSet('request', ele);
})
requestGet.forEach(ele => {
delegateGet('request', ele);
})
responseSet.forEach(ele => {
delegateSet('response', ele);
})
responseGet.forEach(ele => {
delegateGet('response', ele);
})
module.exports = proto;
./src/application.js
const request = require('./request');
const response = require('./response');
const context = require('./context');
this.context = context;
this.request = request;
this.response = response;
responseBody (ctx) {
let content = ctx.body;
if (typeof content === 'string') {
ctx.res.end(content);
} else if (typeof content === 'object') {
ctx.res.end(JSON.stringify(content));
}
}
callback() {
return (req, res) => {
const ctx = this.createContext(req, res);
let respond = () => this.responseBody(ctx);
let onError = (err) => this.onError(err, ctx);
let fn = this.compose();
return fn(ctx).then(respond).catch(onError)
}
}
参考1: https://juejin.im/post/5be3a0a65188256ccc192a87
参考2:https://juejin.im/post/5914fdce44d904006c44dfac