基本组成
- application.js:框架入口;负责管理中间件,以及处理请求
- context.js:context对象的原型,代理request与response对象上的方法和属性
- request.js:request对象的原型,提供请求相关的方法和属性
- response.js:response对象的原型,提供响应相关的方法和属性
开始
新建test.js文件
1 | const koa = require('koa'); |
通过node --inspect-brk test运行,进入调试模式。

浏览器打开http://127.0.0.1:9229/,点击这个

首先通过new Koa()创建实例
1 | module.exports = class Application extends Emitter { |
对象实例:

app.use方法
1 | use(fn) { |
- 判断了传入的
fn类型是否是function - 然后判断是否是
generator函数 - 之后
push进middleware数组里
app.listen方法
1 | listen(...args) { |
- 先执行
this.callback(),返回一个handleRequest函数
1 | callback() { |
compose是由一个名为koa-compose的库,它可以将多个中间件组合成洋葱式调用的对象。
1 | function compose (middleware) { |
调用
http.createServer,该函数用来创建一个HTTP服务器,并将handleRequest作为request事件的监听函数。server.listen(...args)启动服务器监听连接,3000端口号
发送请求到http://localhost:3000/,执行回调函数

1 | const handleRequest = (req, res) => { |
- 首先调用
createContext创建上下文对象
1 | createContext(req, res) { |
- 调用
this.handleRequest(ctx, fn)
1 | handleRequest(ctx, fnMiddleware) { |
进入fnMiddleware(ctx).then(handleResponse).catch(onerror);
1 | return function (context, next) { |
小结
1)首先先创建koa实例
2)app.use是往middleware数组里push进回调函数
3)app.listen启动服务端器监听,并且添加了回调函数
4)触发接口请求回调函数的时候,会将创建的上下文对象与next(next其实就是dispatch方法),作为参数传入到middleware中每项的函数调用中,如果调用了next就是调用的dispatch.bind(null, i + 1)
koa-router
1 | const koa = require('koa'); |
看实例化Router的过程之前,先看一下,遍历数组为Router构造函数添加prototype属性

看Router构造函数
1 | function Router(opts) { |
Router的实例

router.get方法
1 | Router.prototype[method] = function (name, path, middleware) { |
在这里调用register并且传入path、method、name和middleware(这里有对router.get传入的值做了一些修改),进入regiseter方法
1 | Router.prototype.register = function (path, methods, middleware, opts) { |
这里实例化了route,并且push到了stack里
app.use(router.routes())
先看router.routes()
1 | Router.prototype.routes = Router.prototype.middleware = function () { |
声明dispatch方法并且把Router实例挂载到了这个方法上
之后调用app.use把dispatch方法push进去了middleware数组
app.listen方法
与上面一样,就是传入回调,启动个服务。
访问http://localhost:3000/list/1触发回调
进入回调函数
1 | return function (context, next) { |
在这里,取得middleware的第一个回调并且调用
1 | var dispatch = function dispatch(ctx, next) { |
最后调用到compose方法,传入的layerChina是个数组,第一个是匿名函数
1 | function(ctx, next) { |
第二个是访问/list的回调函数

最后又调用进这个方法
1 | return function (context, next) { |
也就是先调用了上面的匿名函数,这个匿名函数为上下文对象添加了一些属性

之后调用next方法,走到我们为访问/list添加的回调那里

小结
1)创建Router实例
2)router.get,注册路由规则,并且push进stack数组
3)app.use(router.routes()),将router.routes()返回的dispatch方法push到middleware数组里
4)app.listen(3000),添加回调函数,并且启动服务器监听
5)访问http://localhost:3000/list/1,调用middleware数组中第一个函数调用,这个函数调用返回compose(layerChain)(ctx, next);
6)layerChain是由两个函数组成的数组,里面的第一个函数被调用时候,解析path,为ctx上下文对象添加一些属性,之后调用next()
7)也就是走进layerChain里的第二个函数调用,也就是router.get的回调函数
koa-cors
test.html
1 |
|
test.js
1 | const koa = require('koa'); |
app.use(cors())调用
先进入cors()
1 | module.exports = function crossOrigin(options = {}) { |
返回了一个异步函数,之后app.use把这个函数push到middleware数组里
刷新html页面,触发send方法,发送请求,触发回调,之后先调用middleware数组里第一个函数调用,就是上面的这个方法,这个方法(在这个例子中)做的事情:
- 获取
origin,通过ctx.set('Access-Control-Allow-Origin', origin);设置头部 - 调用
next()
这里的next调用就是路由Router.routes()返回的那个方法,调用步骤与上面路由的地方一样
小结
1)app.use(cors())把回调传入middleware数组
2)发送请求,设置响应头
因为这边是简单请求,所以没有OPTIONS预检请求
- 当替换请求方法为
PUT的时候,判断是OPTIONS请求的时候,会设置这个响应头

- 当添加请求头
name属性的时候,会设置这个响应头

koa-bodyparser
1 | ... |
先进入bodyParser()
最后往middleware数组push进去这个函数
1 | async function bodyParser(ctx, next) { |
收到请求后根据不同的content-type调用不同的方法解析请求体,这边使用的是application/x-www-form-urlencoded

获取请求的content-encoding、content-length和编码方式utf-8
- 先根据
contentn-encoding的值判断是否需要解压,还是直接返回

- 根据编码方式,Buffer转字符串


- 解析完成后返回str

最后挂载在ctx.request.body上
