一個 HTTP 請求進來,誰負責什麼
你的後端每天處理幾千幾萬個 HTTP 請求。每個請求進來,需要有人做這些事:
- 解析 URL,決定「這個請求要去哪個 handler」
- 在 handler 執行之前,做一堆前置動作(驗身份、解析 body、記 log)
- 執行 handler,呼叫商業邏輯
- 格式化 response,送回去
- 如果出錯,攔截例外、回傳統一格式的錯誤訊息
Framework 的核心職責就是把這個流程結構化。 讓你不用每個請求都從零開始寫這些機制,讓新人看到程式碼就知道每個環節在哪裡。
Framework 實際做的四件事
1. 路由(Routing)
路由解決一個最基本的問題:「GET /users/42 要交給哪個函式處理?」
// Express:手動宣告
app.get('/users/:id', UserController.show);
// NestJS:decorator
@Get(':id')
show(@Param('id') id: string) { ... }
// Spring Boot:annotation
@GetMapping("/users/{id}")
public User show(@PathVariable Long id) { ... }三種框架的語法不同,但做的事一樣:把 URL pattern 對應到 handler function。背後的實作通常是 trie 或 radix tree,讓路由查詢的時間複雜度接近 O(1)。
路由還負責:
- 動態參數(
:id、{id})的提取 - HTTP method 的匹配(GET vs POST)
- Route group 和 prefix(
/api/v1/)
2. 中介層(Middleware)
Middleware 是一個可以在 handler 執行前後插入邏輯的機制。Request 進來後,會先通過一條 middleware 鏈,再到達 handler:
Request → [log] → [auth] → [validate] → Handler → [format] → Response
每個 middleware 可以:
- 讀取或修改 request(例如解析 JWT、把
req.user掛上去) - 提早回傳 response(例如 auth 失敗就直接回 401)
- 把執行權傳給下一個 middleware(Express 的
next()、Koa 的await next())
不同框架的 middleware 模型有微妙差異——這個主題在 Middleware 模型比較 詳細展開。
3. 依賴注入(Dependency Injection)
這是框架差異最大的地方。
有 DI 容器的框架(NestJS、Spring Boot、ASP.NET Core):你宣告「這個 Service 需要那個 Repository」,框架幫你建立實例、管理生命週期、注入依賴。你不需要手動 new 任何東西。
// NestJS:框架管理依賴
@Injectable()
class UserService {
constructor(private userRepo: UserRepository) {} // 框架注入
}沒有 DI 容器的框架(Express、Gin、Fastify):你自己決定怎麼建立依賴、怎麼傳遞。常見做法是手動建立 singleton、或用 closure 把依賴帶進去。
// Express:手動管理
const userRepo = new UserRepository(db);
const userService = new UserService(userRepo);
const userController = new UserController(userService);沒有 DI 不代表不好,但代表你要自己設計依賴管理的方式。DI 的概念在 依賴注入 完整解釋。
4. 生命週期管理(Lifecycle)
Production backend 需要正確地「啟動」和「關閉」:
- 啟動:middleware 先掛好、routes 先登記、DB 連線確認後再開始接請求
- 關閉:收到 SIGTERM 後,不再接新請求、等 in-flight request 處理完、依序關閉 DB pool / Redis / Queue
不同框架對啟動和關閉的控制程度不一樣:
| Framework | 啟動 hook | 關閉 hook |
|---|---|---|
| Express | 無內建(自己處理 process.on('SIGTERM')) | 無內建 |
| NestJS | onModuleInit(), onApplicationBootstrap() | onModuleDestroy(), beforeApplicationShutdown() |
| Spring Boot | ApplicationContext 啟動、@PostConstruct | @PreDestroy, DisposableBean |
| FastAPI | @app.on_event('startup'), lifespan context | @app.on_event('shutdown') |
Express 什麼都不給,你要自己寫 process.on('SIGTERM', gracefulShutdown)。Spring Boot 有完整的應用程式生命週期管理。兩者不是好壞,是你願意管多少事的選擇。
Framework 不做的事
明確一下範圍,避免把框架搞成萬能工具箱:
- 不負責資料庫操作:ORM(Sequelize / TypeORM / JPA / Eloquent)是另一層,框架只是讓你更容易整合
- 不負責商業邏輯:controller 呼叫 service,service 做邏輯——這部分框架沒有意見
- 不負責部署:Docker / K8s / PM2 是部署層,框架只管 HTTP 處理
為什麼「知道 framework 做了什麼」很重要
換框架的時候,你才知道哪些東西要自己補。
從 Express 換 NestJS:你得學 Module / Injectable / Guard,因為 NestJS 把 DI 和生命週期管理都做進去了,你之前自己手寫的那一層要換成框架的方式。
從 Django 換 FastAPI:你得學 Depends(),因為 FastAPI 的 DI 模型跟 Django 完全不同。
從 Spring Boot 換 Ktor:你得接受很多 Spring 幫你管的事(AOP / Bean lifecycle)現在要自己寫,或找對應的 Ktor plugin。
接下來
了解 framework 做什麼之後,下一個問題是:為什麼不直接用 stdlib 就好。這個問題的答案讓你真正感受到 framework 的邊界在哪裡。
