Koa源碼閱讀(一)從搭建Web服務器提及

先複習一下使用原生 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.jscontext.js 封裝了請求和響應做爲上下文 ctx,而 request.js(請求)和 response.js(響應)則爲 context.js 提供支持。this

核心文件是 application.js,主要是兩個方法:spa

1. app.listen() - 監聽端口

封裝並不複雜,僅僅是將原生 Node.js 啓動 Web 服務器的操做放在了一個函數裏。設計

listen(...args) {
  const server = http.createServer(this.callback());
  return server.listen(...args);
}
複製代碼

看到這裏大概也能猜出來,咱們的邏輯(處理請求和響應)都在 this.callback() 裏面。這也是後面要講的重頭戲。code

2. app.use() - 添加中間件

除去校驗參數合法性外,真正實現功能的只有一句: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 中將響應默認置爲404
  • 定義錯誤處理函數 onerror,具體會由 ctx.onerror() 執行
  • 定義響應處理函數 handleResponse,具體會由 this.respond() 執行
  • 調用中間件 「捏」 成的單個回調函數 fn 處理上下文 ctx,其返回一個 Promise 對象,在其then中發出響應(調用 handleResponse),若出錯則處理錯誤(調用 onerror

總結

總的來講,能夠將由 Koa 搭建的 Web 服務器的工做原理分爲兩個過程:

1. 啓動服務器

利用 this.callback() 將中間件 「捏」 成一個回調函數傳給 http.createServer,同時實例化了一個 Server 對象,調用其 listen 方法啓動服務器。

2. 處理請求並響應

this.callback() 返回的是一個回調函數,每一個新的請求到來,Server 就會調用它並傳入請求和響應兩個參數。它會建立包含 req 和 res 的上下文 ctx,並調用回調函數 fn 處理 ctx,繼而發出響應或錯誤。而 fn 是由咱們調用 app.use() 傳入的中間件 「捏」 成的。也就是說,中間件處於核心位置,根據咱們想要的邏輯處理請求和響應。

相關文章
相關標籤/搜索