Express 是一個基于 Node.js 平臺的輕量級、靈活的 Web 應用框架,它為構建 Web 應用和 API 提供了一系列強大的功能。
核心特性
-
中間件支持:Express 使用中間件(middleware)函數來處理 HTTP 請求和響應。中間件可以訪問請求對象(req)、響應對象(res),以及應用程序的請求-響應循環中的下一個中間件函數。通過中間件,你可以執行各種任務,如日志記錄、解析請求體、路由處理等。
-
路由:Express 提供了簡潔而靈活的路由機制,允許你根據不同的 HTTP 方法(GET, POST 等)和 URL 路徑定義處理邏輯。你可以創建復雜的路由結構,并且支持動態路由參數。
-
視圖系統:Express 支持多種模板引擎,如 EJS、Pug、Handlebars 等,使得你可以輕松地生成 HTML 頁面。
-
錯誤處理:內置錯誤處理機制,可以通過特定的中間件來捕獲并處理應用中的錯誤。
-
與數據庫集成:雖然 Express 本身不綁定任何數據庫,但其靈活性意味著它可以輕松地與 MongoDB、MySQL、PostgreSQL 等數據庫集成。
快速入門
安裝 Express:
npm install express --save
創建一個簡單的服務器:
const express = require('express');
const app = express();app.get('/', (req, res) => {res.send('Hello World!');
});app.listen(3000, () => {console.log('Server is running on port 3000.');
});
Express兼容原生http響應
Express 中,響應對象(res)是對 Node.js 原生 http.ServerResponse 對象的擴展,因此它兼容原生 HTTP 的響應方法。這意味著你可以使用原生的方法,也可以使用由 Express 提供的更高級的 API 來發送響應。
//原生 Node.js:
//使用 res.statusCode、res.setHeader、res.writeconst express = require('express');
const http = require('http');const app = express();// Express 中間件處理
app.use((req, res, next) => {// 原生 HTTP 響應方法示例res.statusCode = 200;res.setHeader('Content-Type', 'text/plain');res.write('Hello from Express with native HTTP!');// 繼續 Express 流程next();
});// Express 路由處理
app.get('/', (req, res) => {// 可以繼續使用 Express APIres.end(' (with Express finish)');
});app.listen(3000, () => {console.log('Server running on port 3000');
});
原生 Node.js: 需要手動設置狀態碼為 3xx 并設置 Location
頭。
response.writeHead(302, {Location: 'http://example.com'});
response.end();
Express: 使用便捷的 .redirect()
方法。
res.redirect('http://example.com');
Express 提供了便捷的方法如 .download()
和直接使用流(Stream)來響應文件下載請求。
這是 Express 特有的方法,用于觸發文件下載,并自動設置適當的響應頭(如 Content-Disposition: attachment
)。
const express = require('express');
const app = express();
const path = require('path');app.get('/download', (req, res) => {const filePath = path.join(__dirname, 'public', 'example.txt');res.download(filePath); // 自動觸發下載
});
參數說明:
res.download(filePath, [filename], [options], [callback])
filePath
: 文件在服務器上的路徑。[filename]
: 客戶端看到的文件名(可選,默認為原始文件名)。[options]
: 可選配置對象,例如?{ headers: { ... } }
。[callback]
: 下載完成后的回調函數。
原生 http
的方法時,你需要手動控制所有流程,包括讀取文件、設置響應頭、發送數據等
// const http = require('http');
const fs = require('fs');
const path = require('path');
const express = require('express');const app = express();// const server = http.createServer((req, res) => {
// if (req.url === '/download') {
// const filePath = path.join(__dirname, 'example.txt');// res.writeHead(200, {
// 'Content-Type': 'application/octet-stream',
// 'Content-Disposition': 'attachment; filename="example.txt"'
// });// const readStream = fs.createReadStream(filePath);
// readStream.pipe(res);
// } else {
// res.writeHead(404);
// res.end('Not Found');
// }
// });// server.listen(3000, () => {
// console.log('Server running on port 3000');
// });app.get('/download', (req, res) => {const filePath = path.join(__dirname, 'example.txt');res.writeHead(200, {'Content-Type': 'application/octet-stream','Content-Disposition': 'attachment; filename="example.txt"'});const readStream = fs.createReadStream(filePath);readStream.pipe(res);
});app.listen(3000, () => {console.log('Server running on port 3000');});
express 特有的響應客戶端方法
res.send()
自動根據傳入數據類型設置合適的響應頭,并發送響應體。
- 如果是字符串:默認設置?
Content-Type: text/html
- 如果是對象或數組:自動調用?
JSON.stringify()
,并設置?Content-Type: application/json
- 支持 Buffer 數據(如二進制)
res.send('Hello World'); // 發送 HTML 或純文本
res.send({ name: 'Tom' }); // 自動轉為 JSON 并設置 Content-Type: application/json
res.send(Buffer.from('ABC')); // 發送二進制數據
- 智能處理不同類型的數據。
- 不會觸發瀏覽器下載行為。
- 適合返回 API 響應、HTML 頁面等。
res.json()
專門用于發送 JSON 格式的數據,自動調用 JSON.stringify()
并設置 Content-Type: application/json
。
res.json({ success: true, data: { id: 1 } });
// 輸出:
// {"success":true,"data":{"id":1}}
-
保證響應內容為標準 JSON 格式。
- 設置正確的?
Content-Type
。 - 推薦用于構建 RESTful API。
?? 注意:如果傳入的是一個循環引用的對象,
json()
會拋出錯誤。
res.sendFile()
用于發送一個文件給客戶端(例如 HTML 文件、圖片等),常用于靜態資源服務。
const path = require('path');app.get('/', (req, res) => {res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
-
必須傳入文件的絕對路徑(推薦使用?
path.join()
?構造)。 - 自動設置適當的 MIME 類型(如?
text/html
,?image/png
?等)。 - 不會強制瀏覽器下載,而是嘗試在瀏覽器中直接顯示(如 PDF、圖片等)。
?res.status(code)
- 用途:設置 HTTP 狀態碼。通常與其他響應方法鏈式使用。
-
res.status(404).send('Not Found');
?res.set(field [, value])
?或?res.header(field [, value])
- 用途:設置 HTTP 響應頭。
-
res.set('Content-Type', 'text/plain'); // 或者 res.set({'Content-Type': 'text/plain','ETag': '12345' });
?res.type(type)
?或?res.contentType(type)
- 用途:設置 Content-Type 響應頭。
type
?參數可以是 MIME 類型或文件擴展名。 -
res.type('.html'); // Content-Type 設置為 text/html res.type('json'); // Content-Type 設置為 application/json
路由(Routing)
處理不同路徑和 HTTP 方法的請求。
基本路由
// GET 請求
app.get('/users', (req, res) => { /* ... */ });// POST 請求
app.post('/users', (req, res) => { /* ... */ });// 動態路由參數
app.get('/users/:id', (req, res) => {const userId = req.params.id;res.send(`User ${userId}`);
});
路由分組
使用?express.Router
?創建模塊化路由:
// routes/users.js
const router = require('express').Router();router.get('/', (req, res) => { /* 獲取所有用戶 */ });
router.post('/', (req, res) => { /* 創建用戶 */ });module.exports = router;// main.js
const userRoutes = require('./routes/users');
app.use('/api/users', userRoutes);
?中間件(Middleware)
Express 中,中間件是處理 HTTP 請求的核心機制,它允許你在請求到達路由處理函數之前或之后執行代碼。中間件函數可以:
- 修改請求和響應對象(如添加屬性、設置頭部)
- 結束請求 - 響應循環(如返回錯誤或結果)
- 調用下一個中間件函數(通過?
next()
)
內置中間件
// 解析 JSON 請求體
app.use(express.json());// 靜態文件服務
app.use(express.static('public'));//有時你可能希望為靜態文件添加一個路徑前綴,可以通過將掛載點作為第一個參數傳遞給 express.static 實現:
app.use('/static', express.static('public'));
// 現在,你需要通過 /static/example.html 來訪問 public 目錄下的 example.html 文件//默認情況下,如果 express.static 找不到請求的文件,它不會發送下一個中間件。如果你想改變這種行為,可以讓它繼續執行后續的中間件:app.use(express.static('public', { fallthrough: true }));app.use((req, res, next) => {res.status(404).send('Sorry, we cannot find that!');
});
// 這將確保當靜態文件未找到時,仍然有機會返回自定義的 404 頁面或其他內容。
自定義中間件
中間件函數接收三個參數:(req, res, next)
req
:HTTP 請求對象res
:HTTP 響應對象next
:指向下一個中間件的函數,必須調用它才能繼續執行后續中間件
// 日志中間件
app.use((req, res, next) => {console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);next(); // 傳遞控制權給下一個中間件
});// 錯誤處理中間件(必須有 4 個參數)
app.use((err, req, res, next) => {console.error(err);res.status(500).send('Internal Server Error');
});
中間件的類型
1.?應用級中間件
綁定到?app
?實例,處理所有路由請求:
// 記錄所有請求的時間戳
app.use((req, res, next) => {req.requestTime = Date.now();next();
});
2.?路由級中間件
綁定到特定路由,只處理匹配的請求:
// 僅處理 /users 路徑的請求
app.use('/users', (req, res, next) => {console.log('Processing user-related request');next();
});
3.?錯誤處理中間件
接收四個參數?(err, req, res, next)
,用于捕獲和處理錯誤:
app.use((err, req, res, next) => {console.error(err.stack);res.status(500).send('Internal Server Error');
});
錯誤處理
錯誤處理中間件與普通中間件類似,但需要四個參數,其簽名如下:(err, req, res, next)。即使你不使用 next 參數,也必須指定它,以便 Express 能夠識別這是一個錯誤處理中間件。
統一處理應用中的異常:
app.get('/users',(req, res) => {try {const data = getData();req.data = data;next();} catch (err) {next(err); // 將錯誤傳遞給錯誤處理中間件}
});// 全局錯誤處理
app.use((err, req, res, next) => {const statusCode = err.statusCode || 500;res.status(statusCode).json({error: {message: err.message,status: statusCode}});
});
關鍵點解釋
- 觸發錯誤:在任何地方調用?
next()
?并傳入一個錯誤對象,就可以將控制權交給第一個匹配的錯誤處理中間件。 - 錯誤處理中間件:使用?
app.use()
?來定義,它會捕捉所有通過?next(err)
?傳遞過來的錯誤。 - 環境變量判斷:在這個例子中,根據應用程序環境(
development
?或?production
),決定是否向客戶端暴露詳細的錯誤信息。這有助于保護生產環境下的敏感信息泄露。