先複習一下使用原生 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()
傳入的中間件 「捏」 成的。也就是說,中間件處於核心位置,根據咱們想要的邏輯處理請求和響應。