無框架的 Node.js 伺服器
本文展示了一個使用 Node.js 構建的靜態檔案伺服器,未使用任何框架。Node.js 目前的狀態是,幾乎所有我們需要的靜態檔案伺服器功能都由內建 API 和少量程式碼提供。
示例
使用 Node.js 構建的靜態檔案伺服器
js
import * as fs from "node:fs";
import * as http from "node:http";
import * as path from "node:path";
const PORT = 8000;
const MIME_TYPES = {
default: "application/octet-stream",
html: "text/html; charset=UTF-8",
js: "text/javascript",
css: "text/css",
png: "image/png",
jpg: "image/jpeg",
gif: "image/gif",
ico: "image/x-icon",
svg: "image/svg+xml",
};
const STATIC_PATH = path.join(process.cwd(), "./static");
const toBool = [() => true, () => false];
const prepareFile = async (url) => {
const paths = [STATIC_PATH, url];
if (url.endsWith("/")) paths.push("index.html");
const filePath = path.join(...paths);
const pathTraversal = !filePath.startsWith(STATIC_PATH);
const exists = await fs.promises.access(filePath).then(...toBool);
const found = !pathTraversal && exists;
const streamPath = found ? filePath : `${STATIC_PATH}/404.html`;
const ext = path.extname(streamPath).substring(1).toLowerCase();
const stream = fs.createReadStream(streamPath);
return { found, ext, stream };
};
http
.createServer(async (req, res) => {
const file = await prepareFile(req.url);
const statusCode = file.found ? 200 : 404;
const mimeType = MIME_TYPES[file.ext] || MIME_TYPES.default;
res.writeHead(statusCode, { "Content-Type": mimeType });
file.stream.pipe(res);
console.log(`${req.method} ${req.url} ${statusCode}`);
})
.listen(PORT);
console.log(`Server running at http://127.0.0.1:${PORT}/`);
分解
以下程式碼行匯入了 Node.js 的內部模組。
js
import * as fs from "node:fs";
import * as http from "node:http";
import * as path from "node:path";
接下來是用於建立伺服器的函式。https.createServer 返回一個 Server 物件,我們可以透過監聽 PORT 來啟動它。
js
http
.createServer((req, res) => {
/* handle http requests */
})
.listen(PORT);
console.log(`Server running at http://127.0.0.1:${PORT}/`);
非同步函式 prepareFile 返回以下結構:{ found: boolean, ext: string, stream: ReadableStream }。如果檔案可以被服務(伺服器程序有訪問許可權且未發現路徑遍歷漏洞),我們將返回 200 的 HTTP 狀態碼作為 statusCode,表示成功(否則返回 HTTP 404)。請注意,其他狀態碼可以在 http.STATUS_CODES 中找到。對於 404 狀態,我們將返回 '/404.html' 檔案的內容。
請求檔案的副檔名將被解析並轉換為小寫。然後,我們將搜尋 MIME_TYPES 集合以查詢正確的 MIME 型別。如果未找到匹配項,我們將使用 application/octet-stream 作為預設型別。
最後,如果沒有錯誤,我們將傳送請求的檔案。file.stream 將包含一個 Readable 流,該流將被管道傳輸到 res(Writable 流的一個例項)。
js
res.writeHead(statusCode, { "Content-Type": mimeType });
file.stream.pipe(res);