先复习一下使用原生 Node.js 搭建一个 Web 服务器。服务器
var http = require('http');
var server = http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'})
res.end('Hello world\n')
})
server.listen(3000)
复制代码
能够看到,咱们只须要关注 http.createServer()
传入的回调函数和 server.listen()
传入的参数便可。通常来说, server.listen()
传入 Web 服务器监听的端口号,而 http.createServer()
传入的回调函数则负责处理 HTTP 请求并给出响应。app
相同的逻辑对应到 Koa 上来,代码量差很少。koa
const koa = require('koa');
const app = new koa();
app.use(ctx => {
ctx.body = 'Hello world'
})
app.listen(3000)
复制代码
仔细观察咱们发现,server.listen
对应于 app.listen
,而 http.createServer()
传入的回调函数在 Koa 里则是利用 app.use()
传入的。实际上,处理请求和响应的操做就是由 app.use()
传入的函数完成的。函数
基于这个思路,咱们能够开始分析 Koa 源码中涉及到上面描述的部分。ui
Koa 的源码只有四个文件。其中,负责对外暴露方法的是 application.js
,context.js
封装了请求和响应做为上下文 ctx
,而 request.js
(请求)和 response.js
(响应)则为 context.js
提供支持。this
核心文件是 application.js
,主要是两个方法:spa
封装并不复杂,仅仅是将原生 Node.js 启动 Web 服务器的操做放在了一个函数里。设计
listen(...args) {
const server = http.createServer(this.callback());
return server.listen(...args);
}
复制代码
看到这里大概也能猜出来,咱们的逻辑(处理请求和响应)都在 this.callback()
里面。这也是后面要讲的重头戏。code
除去校验参数合法性外,真正实现功能的只有一句:server
use(fn) {
// ...
this.middleware.push(fn);
// ...
}
复制代码
实际上就是将传入的中间件函数添加到 this.middleware
中。最终,就是这些中间件函数,构成了处理请求和响应的绝大多数逻辑。
文件开始的时候,咱们已经获得一个思路,http.createServer()
传入的回调函数负责处理每一个 HTTP 请求并给出响应,而如今咱们发现传入的是 this.callback()
的返回值,咱们来看看它的代码。
callback() {
const fn = compose(this.middleware);
// ...
const handleRequest = (req, res) => {
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};
return handleRequest;
}
复制代码
返回的 handleRequest
局部变量就是咱们一直提到的那个回调函数,它与原生 Node.js 搭建的服务器同样,接收请求(req)和响应(res)两个参数。每次请求到来时,这个函数都会被调用,它完成两个工做:
ctx
,封装了本次的请求和响应ctx
和函数 fn
交由 this.handleRequest()
处理对了,这个函数的第一行咱们没有介绍,它用到了 app.use()
传进来的中间件 this.middleware
。
const fn = compose(this.middleware);
复制代码
中间件机制是 Koa 设计中很是巧妙的一部分,利用中间件咱们能够为 Web 服务器提供各类各样的功能。鉴于篇幅,咱们只介绍如何把传入的多个中间件变成咱们想要的回调函数。
这里用到的是 koa-compose
这个 NPM 包,它把传入的多个中间件 "捏" 成一个回调函数 fn
,由它对上下文 ctx
进行处理,固然也就是 HTTP 请求和响应。
上节提到,上下文 ctx
和函数 fn
交给了 this.handleRequest()
处理,它进行了如下几项工做:
ctx
中将响应默认置为404onerror
,具体会由 ctx.onerror()
执行handleResponse
,具体会由 this.respond()
执行fn
处理上下文 ctx
,其返回一个 Promise 对象,在其then中发出响应(调用 handleResponse
),若出错则处理错误(调用 onerror
)总的来讲,能够将由 Koa 搭建的 Web 服务器的工做原理分为两个过程:
利用 this.callback()
将中间件 “捏” 成一个回调函数传给 http.createServer
,同时实例化了一个 Server
对象,调用其 listen
方法启动服务器。
this.callback()
返回的是一个回调函数,每一个新的请求到来,Server
就会调用它并传入请求和响应两个参数。它会建立包含 req 和 res 的上下文 ctx
,并调用回调函数 fn
处理 ctx
,继而发出响应或错误。而 fn
是由咱们调用 app.use()
传入的中间件 “捏” 成的。也就是说,中间件处于核心位置,根据咱们想要的逻辑处理请求和响应。