前言
- 找到koa源码的package.json文件,找到”main”: “lib/application.js”,此时我们调用的Koa的类就是在application.js中的
- application主要做的事情就是实例化应用,
- context主要做的事情就是实例上下文,
- request.js由原生request事件的http.IncomingMessage类过滤而来;
- response.js对应ctx.response,由原生request事件的http.ServerResponse类过滤而来。
处理中间件
我们调用中间件的时候传入的callback,callback循环被组合的中间件,递归调用中间件 ```javascript /**
- Shorthand for: *
- http.createServer(app.callback()).listen(…) *
- @param {Mixed} …
- @return {Server}
- @api public */
listen(…args) { debug(‘listen’); const server = http.createServer(this.callback()); return server.listen(…args); }
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;
}
<a name="f7n8H"></a>#### context->ctx别名处理- koa内部使用的是proto.__defineSetter__和proto.__defineGetter__ [__defineGetter__(mdn已废弃)](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/__defineGetter__)- 也可以使用Object.defineProperty```javascriptconst context = {// get method() {// return this.request.method// },// get url(){// return this.request.url// }}defineProperty('request', 'method')defineProperty('request', 'url')defineProperty('request', 'body')function defineProperty(target, name) {// context.__defineGetter__(name, function () {// return this[target][name]// })Object.defineProperty(context, name, {get() {return this[target][name]},set(value) {this[target][name] = value}})}module.exports = context
简单的实现
application.js
洋葱结构,当初为什么要这样设计呢?因为是为了解决复杂应用中频繁的回调而设计的级联代码,它并不会把控制权完全交给一个中间件的代码,而是碰到next就会去下一个中间件,等下面所有中间件执行完成后,就会再回来执行中间件未完成的代码。
为什么要返回promise呢
所以Promise.resolve的作用, 在普通函数中依旧能使用next().then()的形式,保证中间件的正确运行
- 如果所有的中间件都为普通函数并且没有异步,洋葱模型的实现与调用栈有关,
由于async,await的特性,所有的中间件都使用async/await 也可以保持异步的情况下保持洋葱模型,
此时都可以使用compose-sync 达到与compose-async同样的效果
但当要在普通函数中实现异步保持洋葱模型,则需要compose-async 返回promise, 以达到与async/await 同样的效果。
const http = require('http')const context = require('./context')const request = require('./request')const response = require('./response')class Application {constructor() {// 保存用户添加的中间件函数this.middleware = []// 为了方便实用,直接添加到application实例上// 如果有多个实例共享的时候,数据容易受到污染this.context = Object.create(context)this.request = Object.create(request)this.response = Object.create(response)}listen(...args) {// 把相关逻辑抽离出去const server = http.createServer(this.callback())server.listen(...args)}// 当调用use的时候,调用的处理函数(中间件),往middleware中push中间件use(fn) {this.middleware.push(fn)}// 异步递归遍历调用中间件处理函数 nextcompose(middleware) {return function (context) {const dispatch = index => {// next就是dispatch,处理index边界问题// 整个返回的都是promise,所以在超出边界的时候也要返回promise.resolve成功的状态if (index >= middleware.length) return Promise.resolve()const fn = middleware[index]// 把fn强制的包装到一个promise里面return Promise.resolve(// 上下文对象fn(context, () => dispatch(index + 1)) // 这是next函数)}// 返回第一个中间件处理函数// dispatch不是一数组。是一个函数,调用dispatchreturn dispatch(0)}}// 创建上下文对象的函数// 构造上文对象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;console.log(req);context.originalUrl = request.originalUrl = req.url;context.state = {};return context;}callback() {// compose进行递归遍历,把当前存储中间件函数的数组存储进来// 扩展性更好,middleware可以通过参数传进来const fnMiddleware = this.compose(this.middleware)const handleRequest = (req, res) => {// 最终对应的结果// 每个请求都会创建一个独立的context对象,他们之间不会互相污染const context = this.createContext()fnMiddleware(context).then(() => {// console.log('end');res.end(context.body)// res.end('My Koa')}).catch(err => {res.end(err.message)})}return handleRequest}}module.exports = Application
context
const context = {// get method() {// return this.request.method// },// get url(){// return this.request.url// }}defineProperty('request', 'method')defineProperty('request', 'url')defineProperty('request', 'body')function defineProperty(target, name) {// context.__defineGetter__(name, function () {// return this[target][name]// })Object.defineProperty(context, name, {get() {return this[target][name]},set(value) {this[target][name] = value}})}module.exports = context
response
const response = {set status(val) {this.res.statusCode = val},_body: '', // 真正用来存储数据的get body() {return this._body},set body(value) {this._body = value}}module.exports = response
request
const url = require('url')const request = {// 对象属性访问器get method() {console.log(this);return this.req.method},get header() {return this.req.headers},get url() {return this.req.url},get path() {return url.parse(this.req.url).pathname},get query() {return url.parse(this.req.url, true).query}}module.exports = request
