Koa 洋葱模型
Koa Http Server
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(3000);
Node Http Server
const http = require("http");
const server = http.createServer((req, res) => {
const stream = fs.createReadStream('file.txt');
stream.on('data', (chunk) => {
res.write(chunk); // Write data to the response
});
stream.on('end', () => {
res.end(); // End the response
});
});
server.listen(3003, () => {
console.log("Server running at localhost:3003");
});
洋葱模型实现
app.use
class 初始化一个 middleware 数组,use 的时候把function push到队列
use(fn) {
this.middleware.push(fn);
return this;
}
2.app.listen
node http 模块创建一个HttpServer,传入相应的 callback
listen(...args) {
const server = http.createServer(this.callback());
return server.listen(...args);
}
// http.createServer((req,res) => {}) 接受2个参数,req, res
- HttpServer callback函数
callback
实现
callback() {
// 重点是 compose 函数实现
const fn = compose(this.middleware);
// 此处返回一个 callback 接受 req,res 2个参数
const handleRequest = (req, res) => {
// 创建 middleware context
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};
return handleRequest;
}
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, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
}
- handleRequest
handleRequest(ctx, fnMiddleware) {
// promise
return fnMiddleware(ctx)
.then(handleResponse)
.catch(onerror);
}
- respond
Javascript
const stream = fs.createReadStream('file.txt');
stream.on('data', (chunk) => {
res.write(chunk); // Write data to the response
});
stream.on('end', () => {
res.end(); // End the response
});
// 等价于
const stream = fs.createReadStream('file.txt');
stream.pipe(res)
koa 中对 ctx.body
类型做了判断,stream
类型流式返回
Javascript
const stream = fs.createReadStream('file.txt');
stream.on('data', (chunk) => {
res.write(chunk); // Write data to the response
});
stream.on('end', () => {
res.end(); // End the response
});
Koa 源码
class Koa extends Emitter {
constructor (options) {
this.middleware = []
}
use (fn) {
this.middleware.push(fn)
return this
}
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
}
listen (...args) {
const server = http.createServer(this.callback())
return server.listen(...args)
}
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)
}
}
Koa 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!')
}
/**
* @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)
}
}
}
}