node http 的用法很简单,http 的每次请求,都会带有 request 和 response 两个对象。
const server = http.createServer((request, response) => { // request 可以获取到 http 请求头相关的东西 console.log(request.method); console.log(request.url); console.log(request.headers['cookie']); console.log(request.httpVersion); // response 响应流 response.write('response data.'); response.end(); }); server.listen(3000); 复制代码
通过 request 可以获取到请求头相关的东西,比如请求头的 method、url、http header 等等,另外,request 继承自可读流,因此 request 具备可读流的所有特点,比如可以监听 on(‘data’) 获取到请求体的内容,也可以on(‘end’) 监听流的结束,代码如下:
const data = []; request.on('data', function (dataBuffer) { data.push(dataBuffer); }); request.on('end', function () { const buffer = Buffer.concat(data).toString(); console.log(buffer); }); 复制代码
response 继承自可写流,拥有可写流的所有特点,比如 write、end 、close操作, 通过 response.wirte(‘响应数据’) + response.end() 可以向客户端响应数据。 response.end(‘xxx’) 相当于 response.write(‘xxx’) + response.close(); 代码如下:
response.write('response data.'); response.end(); 复制代码
koa 是一个 http 框架,因此,koa 是基于 node http 模块的。 最简单的 koa 实现如下(其实内部就是封装了 http):
class Koa { constructor(fn) { this.fn = fn; } listen(...args) { const server = http.createServer((req, res) => { this.fn(req, res); }); return server.listen(...args); } } function handleHttp(req, res) { res.end('response data.'); } const koa = new Koa(handleHttp); koa.listen(3000); 复制代码
function Delegator(proto, target) { if (!(this instanceof Delegator)) { // 这里是为了保证 Delegator 的实例是 new 出来的 return new Delegator(proto, target); } this.proto = proto; this.target = target; } Delegator.prototype.getter = function (name) { const proto = this.proto; const target = this.target; proto.__defineGetter__(name, function () { return this[target][name]; // 当访问 proto[name] 的时候,访问的是 proto[target][name] }); return this; }; Delegator.prototype.setter = function (name) { const proto = this.proto; const target = this.target; proto.__defineSetter__(name, function (value) { return this[target][name] = value; // 当设置 proto[name] 的时候,访问的是 proto[target][name] }); return this; }; Delegator.prototype.access = function (name) { return this.getter(name).setter(name); }; Delegator.prototype.method = function (name) { const proto = this.proto; const target = this.target; proto[name] = function () { // 当调用 proto[name]() 函数的时候,访问的是 proto[target][name]() return this[target][name].apply(target, arguments); }; return this; }; const context = { request: { a: 123, fn: function () { return 'fn'; } } }; Delegator(context, 'request') .getter('a') .setter('a') .method('fn'); console.log(context.a); // 123 console.log(context.fn()); // fn 复制代码
输出如下:
123 fn 复制代码
const request = { get method() { return this.req.method; }, }; const response = { get body() { return this._body; }, set body(val) { this._body = val; }, }; const context = {}; Delegator(context, 'request').getter('method'); Delegator(context, 'response').access('body'); class Koa { constructor(fn) { this.fn = fn; this.context = Object.create(context); this.request = Object.create(request); this.response = Object.create(response); } listen(...args) { const server = http.createServer((req, res) => { const context = this.createContext(req, res); Promise.resolve(this.fn(context)).then(() => { let body = 'koa response'; if (context.body) { body = context.body; } res.write(body); res.end(); }); }); return server.listen(...args); } createContext(req, res) { const context = Object.create(this.context); context.res = res; context.req = req; context.request = Object.create(this.request); context.response = Object.create(this.response); context.req = request.req = response.req = req; context.res = request.res = response.res = res; return context; } } function handleHttp(ctx) { console.log(ctx.method); // ctx.method 会被委托到 ctx.request.method ctx.body = 'reponse data.'; // ctx.body 会被委托到 ctx.response.body } const koa = new Koa(handleHttp); koa.listen(3000); 复制代码
koa 的核心思想在于基于中间件的洋葱模型,koa 通过一个 compose 函数,把一个一个的中间件串联起来,组合成了一个大的函数,在每个中间件里,通过 next() 执行下一个中间件。 引用官方的一张洋葱模型图,如下:
compose 的代码如下:
function compose(middlewares) { return function (context, next) { function dispatch(i) { let fn = middlewares[i]; if (i === middlewares.length) fn = next; if (!fn) { return Promise.resolve(); } try { return Promise.resolve(fn(context, () => { return dispatch(i + 1); })); } catch (e) { return Promise.reject(e); } } return dispatch(0); }; } const middlewares = []; async function a(ctx, next) { console.log('a1'); const r = await next(); console.log(r); console.log('a2'); } async function b(ctx, next) { console.log('b1'); await next(); console.log('b2'); return 'b'; } middlewares.push(a); middlewares.push(b); compose(middlewares)({}).then(v => { console.log('ddd'); }); 复制代码
以上代码的执行顺序如下:
a in b in b out b a out end 复制代码
const http = require('http'); const EventEmitter = require('events') function compose(middlewares) { return function (context, next) { function dispatch(i) { let fn = middlewares[i]; if (i === middlewares.length) fn = next; if (!fn) { return Promise.resolve(); } try { return Promise.resolve(fn(context, () => { return dispatch(i + 1); })); } catch (e) { return Promise.reject(e); } } return dispatch(0); }; } function Delegator(proto, target) { if (!(this instanceof Delegator)) { // 这里是为了保证 Delegator 的实例是 new 出来的 return new Delegator(proto, target); } this.proto = proto; this.target = target; } Delegator.prototype.getter = function (name) { const proto = this.proto; const target = this.target; proto.__defineGetter__(name, function () { return this[target][name]; // 当访问 proto[name] 的时候,访问的是 proto[target][name] }); return this; }; Delegator.prototype.setter = function (name) { const proto = this.proto; const target = this.target; proto.__defineSetter__(name, function (value) { return this[target][name] = value; // 当设置 proto[name] 的时候,访问的是 proto[target][name] }); return this; }; Delegator.prototype.access = function (name) { return this.getter(name).setter(name); }; Delegator.prototype.method = function (name) { const proto = this.proto; const target = this.target; proto[name] = function () { // 当调用 proto[name]() 函数的时候,访问的是 proto[target][name]() return this[target][name].apply(target, arguments); }; return this; }; const request = { get method() { return this.req.method; }, }; const response = { get body() { return this._body; }, set body(val) { this._body = val; }, }; const context = {}; Delegator(context, 'request').getter('method'); Delegator(context, 'response').access('body'); class Koa extends EventEmitter { constructor() { super(); this.middlewares = []; this.context = Object.create(context); this.request = Object.create(request); this.response = Object.create(response); } listen(...args) { const server = http.createServer((req, res) => { this.handleHttp(req, res); }); return server.listen(...args); } use(fn) { this.middlewares.push(fn); } handleHttp(req, res) { const middleware = compose(this.middlewares); const context = this.createContext(req, res); middleware(context).then(() => { let body = 'koa response'; if (context.body) { body = context.body; } res.write(body); res.end(); }); } createContext(req, res) { const context = Object.create(this.context); context.res = res; context.req = req; context.request = Object.create(this.request); context.response = Object.create(this.response); context.req = request.req = response.req = req; context.res = request.res = response.res = res; return context; } } const koa = new Koa(); async function middlewareA(ctx, next) { console.time('responseTime'); console.log('middlewareA in'); console.log(ctx.method); // ctx.method 会被委托到 ctx.request.method await next(); console.log(ctx.body); console.log('middlewareA out'); console.timeEnd('responseTime'); } function middlewareB(ctx, next) { console.log('middlewareB in'); ctx.res.setHeader('Content-Type', 'text/html;charset=utf-8'); ctx.body = 'reponse data.'; // ctx.body 会被委托到 ctx.response.body console.log('middlewareB out'); } koa.use(middlewareA); koa.use(middlewareB); const server = koa.listen(3000); server.on('listening', function () { console.log('listening on localhost:3000'); }); 复制代码
github.com/SimpleCodeC…