純初學者
- 先看 Python 最小伺服器。
- 接著直接看 Express 範例。
- 最後再回頭補原生 Node.js 的底層版本。
真正的學習通常發生在「理論與範例接起來」的那一刻。
本章會用兩種常見語言的最小伺服器範例,讓你看到:程式如何接住 request、決定回應內容、設定 Content-Type,最後把結果交給瀏覽器;如果你原本習慣從 Apache 或 Nginx 的設定檔來理解網站,這一章會幫你把 Web server、Node.js Runtime 與 Framework 的位置重新排好。
http.createServer() 與 app.get() 的差異。| 你熟悉的傳統觀念 | 在現代 Web 架構中的對應 | 例子 |
|---|---|---|
| VirtualHost / server block | 站點入口與反向代理設定 | server_name、listen、location /api |
| CGI / FastCGI / PHP-FPM / uWSGI | 讓應用程式跑起來的執行層(Runtime / Process) | node server.js、uvicorn app:app |
| Rewrite / ProxyPass | 把特定路徑轉送到應用服務 | proxy_pass http://127.0.0.1:3000 |
| 應用程式框架 | 真正處理路由、驗證與商業邏輯的層 | Express、NestJS、FastAPI、Django |
Apache / Nginx + PHP-FPM,可以把 Node.js 想成「讓 JavaScript 應用能跑起來的執行環境」;
而 Express / NestJS 的角色,更接近 Laravel / Symfony 這類框架,負責組織路由、驗證與商業邏輯。
在固定 IP / Port 上等待 request 進來。
根據 URL path 判斷要回傳檔案、資料或錯誤訊息。
設定狀態碼與 Content-Type,讓瀏覽器知道如何處理。
| 內容 | MIME Type | 常見用途 |
|---|---|---|
| HTML | text/html |
網頁結構 |
| CSS | text/css |
樣式表 |
| JavaScript | text/javascript |
前端腳本 |
| JSON | application/json |
API 回應資料 |
| PNG | image/png |
圖片資源 |
這個例子很適合教學,因為它夠短,但已經能看出 request 與 response 的結構。
from http.server import BaseHTTPRequestHandler, HTTPServer
class DemoHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.end_headers()
self.wfile.write(b"<h1>Hello from Python</h1>")
server = HTTPServer(("127.0.0.1", 8000), DemoHandler)
print("Serving on http://127.0.0.1:8000")
server.serve_forever()
send_response(200) 設定狀態碼。send_header() 告訴瀏覽器回傳的是 HTML。wfile.write() 寫出真正的回應內容。http 模組如果你想知道 Express 背後更底層的作法,再回來看這個版本。純初學者可以先看下一個 Express 範例,再回頭比較兩者差異。
const http = require("http");
const server = http.createServer((request, response) => {
response.writeHead(200, {
"Content-Type": "application/json"
});
response.end(JSON.stringify({
message: "Hello from Node.js"
}));
});
server.listen(3000, () => {
console.log("Server on http://127.0.0.1:3000");
});
{
"message": "Hello from Node.js"
}
node server.js 的意思,是由 Node.js 這個 Runtime 去執行你的程式檔。http.createServer() 代表你在 Runtime 裡建立了一個會處理 HTTP 的應用程式。Content-Type 正確,瀏覽器或前端程式就知道怎麼處理。
更詳細地說,Node.js 本身提供的是伺服器端 JavaScript 執行環境,以及檔案系統、網路、計時器、http 等 API。
當你執行 node server.js 時,是 Node.js 這個 Runtime 在執行你的應用程式;
如果你的程式裡再使用 Express 或 NestJS,才是把「如何定義路由、如何插入 middleware、如何切分 controller / service」這些工作交給框架來整理。
| 情境 | 原生 Node.js | Express |
|---|---|---|
| 只有 1 條路由 | 還算容易,手寫 if (req.url === ...) 就能完成。 |
也很簡單,但結構會稍微多一點。 |
| 有 10 條以上路由 | 容易變成很長的條件判斷,維護困難。 | 可用 app.get()、app.post() 與 router 拆分。 |
| 需要 middleware / 驗證 | 很多流程要自己手寫。 | 框架式工具通常已有成熟寫法與慣例。 |
對多數初學者來說,這會比原生 Node.js 更像真正的應用開發,也更容易讀懂。
const express = require("express");
const app = express();
app.get("/api/status", (req, res) => {
res.json({
service: "ok",
runtime: "Node.js",
framework: "Express"
});
});
app.listen(3000, () => {
console.log("Express on http://127.0.0.1:3000");
});
{
"service": "ok",
"runtime": "Node.js",
"framework": "Express"
}
if (request.url === ...) 這種手動判斷包裝成更清楚的路由 API。res.json()、middleware、router 等慣例,都是框架式工具帶來的便利。這一段是讓你回頭對照:如果不用框架,原來每一條路由都要自己手動判斷與回應。
if (request.url === "/") {
response.end("<h1>Home Page</h1>");
} else if (request.url === "/about") {
response.end("<h1>About Page</h1>");
} else {
response.writeHead(404, { "Content-Type": "text/plain" });
response.end("Not Found");
}
| Request URL | 回應結果 |
|---|---|
/ |
Home Page |
/about |
About Page |
/missing |
404 Not Found |
| 層次 | 代表工具 | 主要責任 | 對傳統管理者最容易混淆的點 |
|---|---|---|---|
| 入口層 Web server | Nginx、Apache | 接收連線、TLS、靜態檔、反向代理 | 它仍然存在,並沒有被 Node.js 取代。 |
| 執行層 Runtime | Node.js、Python | 讓應用程式碼能跑起來 | 它負責「執行程式」,不是幫你定義整個應用架構。 |
| 應用層 Framework | Express、NestJS、FastAPI、Django | 整理路由、驗證、middleware、模組與商業邏輯 | 這一層才是你從「手寫 server」進入「框架化開發」的地方。 |
http.createServer() 時,是你的應用程式利用 Runtime 建立了一個 server。