如何从零开始手写Koa2框架(2)

function respond(req, res) { // 获取设置的body数据 let body = res.body; if (typeof body === 'object') { // 如果是对象,转化成json数据返回 body = JSON.stringify(body); res.end(body); } else { // 默认其他数据直接返回 res.end(body); } }

在callback中调用

callback() { const fn = compose(this.middleware); const handleRequest = (req, res) => { // 当中间件函数全部执行完毕时,会触发then方法,从而执行respond方法返回响应 const handleResponse = () => respond(req, res); fn(req, res).then(handleResponse); } return handleRequest; }

修改入口文件 index.js 代码

// 引入自定义模块 const MyKoa = require('./js/my-application'); // 创建实例对象 const app = new MyKoa(); // 使用中间件 app.use((req, res, next) => { console.log('中间件函数执行了~~~111'); next(); }) app.use((req, res, next) => { console.log('中间件函数执行了~~~222'); // 设置响应内容,由框架负责返回响应~ res.body = 'hello myKoa'; }) // 监听端口号 app.listen(3000, err => { if (!err) console.log('服务器启动成功了'); else console.log(err); })

此时我们就能根据不同响应内容做出处理了~当然还是比较简单的,可以接着去扩展~

06、定义 Request 模块

// 此模块需要npm下载 const parse = require('parseurl'); const qs = require('querystring'); module.exports = { /** * 获取请求头信息 */ get headers() { return this.req.headers; }, /** * 设置请求头信息 */ set headers(val) { this.req.headers = val; }, /** * 获取查询字符串 */ get query() { // 解析查询字符串参数 --> key1=value1&key2=value2 const querystring = parse(this.req).query; // 将其解析为对象返回 --> {key1: value1, key2: value2} return qs.parse(querystring); } }

07、定义 Response 模块

module.exports = { /** * 设置响应头的信息 */ set(key, value) { this.res.setHeader(key, value); }, /** * 获取响应状态码 */ get status() { return this.res.statusCode; }, /** * 设置响应状态码 */ set status(code) { this.res.statusCode = code; }, /** * 获取响应体信息 */ get body() { return this._body; }, /** * 设置响应体信息 */ set body(val) { // 设置响应体内容 this._body = val; // 设置响应状态码 this.status = 200; // json if (typeof val === 'object') { this.set('Content-Type', 'application/json'); } }, }

08、定义 Context 模块

// 此模块需要npm下载 const delegate = require('delegates'); const proto = module.exports = {}; // 将response对象上的属性/方法克隆到proto上 delegate(proto, 'response') .method('set') // 克隆普通方法 .access('status') // 克隆带有get和set描述符的方法 .access('body') // 将request对象上的属性/方法克隆到proto上 delegate(proto, 'request') .access('query') .getter('headers') // 克隆带有get描述符的方法

09、揭秘 delegates 模块

module.exports = Delegator; /** * 初始化一个 delegator. */ function Delegator(proto, target) { // this必须指向Delegator的实例对象 if (!(this instanceof Delegator)) return new Delegator(proto, target); // 需要克隆的对象 this.proto = proto; // 被克隆的目标对象 this.target = target; // 所有普通方法的数组 this.methods = []; // 所有带有get描述符的方法数组 this.getters = []; // 所有带有set描述符的方法数组 this.setters = []; } /** * 克隆普通方法 */ Delegator.prototype.method = function(name){ // 需要克隆的对象 var proto = this.proto; // 被克隆的目标对象 var target = this.target; // 方法添加到method数组中 this.methods.push(name); // 给proto添加克隆的属性 proto[name] = function(){ /* this指向proto, 也就是ctx 举个栗子:ctx.response.set.apply(ctx.response, arguments) arguments对应实参列表,刚好与apply方法传参一致 执行ctx.set('key', 'value') 实际上相当于执行 response.set('key', 'value') */ return this[target][name].apply(this[target], arguments); }; // 方便链式调用 return this; }; /** * 克隆带有get和set描述符的方法. */ Delegator.prototype.access = function(name){ return this.getter(name).setter(name); }; /** * 克隆带有get描述符的方法. */ Delegator.prototype.getter = function(name){ var proto = this.proto; var target = this.target; this.getters.push(name); // 方法可以为一个已经存在的对象设置get描述符属性 proto.__defineGetter__(name, function(){ return this[target][name]; }); return this; }; /** * 克隆带有set描述符的方法. */ Delegator.prototype.setter = function(name){ var proto = this.proto; var target = this.target; this.setters.push(name); // 方法可以为一个已经存在的对象设置set描述符属性 proto.__defineSetter__(name, function(val){ return this[target][name] = val; }); return this; };

10、使用 ctx 取代 req 和 res

修改 my-application

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/6bc4f95d9e7437596509e787aa168e7d.html