用 Node.js 實作一個 Apache HTTP Server (一)

Apache 伺服器軟體有一個預設功能,假如我們有一個資料夾結構如下:

1
2
3
4
5
6
7
— server.js
— www
  ∟ index.html
  ∟ file
   ∟ index.html
   ∟ img
    ∟ 6.jpg

在 Apache 中,我們可以通過開啟本地網頁伺服器,並透過 127.0.0.1/index.html 訪問到 index.html,也可以透過 127.0.0.1/file/index.html 訪問到 index.html,另外如果在網址欄輸入不存在的路徑時,會有錯誤頁面。現在讓我們透過 Node.js 初步實現這個功能。

基本架構

server.js

1
2
3
4
5
6
7
var http = require('http')
var fs = require('fs')
var server = http.createServer()
server.on('request', function (req, res) {}
server.listen('8000', function () {
console.log('伺服器正在運行...')
})

設定 url 對應的檔案判斷

目前我們希望可以讓使用者透過網址直接訪問 www 資料夾內的資源,可以透過這樣來達成

1
2
3
4
5
6
7
8
9
10
11
server.on('request', function (req, res) {
var url = req.url
if (url === '/' || url === '/index.html') {
fs.readFile('./www/index.html', function (err, data) {
if (err) {
return res.end('404 Not Found.')
}
res.end(data)
})
}
}

接著把所有資源的判斷式都補上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
server.on('request', function (req, res) {
var url = req.url
if (url === '/' || url === '/index.html') {
fs.readFile('./www/index.html', function (err, data) {
if (err) {
return res.end('404 Not Found.')
}
res.end(data)
})
} else if (url === '/file' || url === '/file/index.html') {
fs.readFile('./www/file/index.html', function (err, data) {
if (err) {
return res.end('404 Not Found.')
}
res.end(data)
})
} else if (url === '/file/img/6.jpg') {
fs.readFile('./www/file/img/6.jpg', function (err, data) {
if (err) {
return res.end('404 Not Found.')
}
res.end(data)
}
}

優化 - 動態讀取

上面這樣的方式在伺服器資源越來越多時,每新增一個檔案就必須要寫一個判斷式,非常沒有效率。我們可以用更聰明的方式來達成一樣的效果。

我們可以將請求路徑的根當作 www 資料夾,依據請求路徑到 www 資料夾找出相對應的檔案,例如請求路徑為 /AAA/B.html,那我們就找找有沒有 www/AAA/B.html 的檔案,若有相應檔案就回應此檔案,若沒有就回應 404 頁面。
實現方式:

  • 先將 ./www 儲存在變數 wwwDir 中,所有資源將會以該資料夾為根目錄選找對應檔案
  • 透過 wwwDir + 請求路徑,就能抓到將對應的檔案
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var wwwDir = './www'

    server.on('request', function (req, res) {
    var url = req.url
    var filePath
    // 在 Apache 中,當請求路徑為 / 時,預設回傳 index.html
    url === '/' ? filePath = '/index.html' : filePath = url

    fs.readFile(wwwDir + filePath, function (error, data) {
    if (error) {
    return res.end('404 Not Found.')
    }
    res.end(data)
    })
    })