node.js 中的 express 框架基礎(chǔ)到進階指南
一、基礎(chǔ)篇:Express 核心概念入門
1. 第一個 Express 應(yīng)用:極簡啟動
先看一個最基礎(chǔ)的 Express 應(yīng)用,理解其核心結(jié)構(gòu):
// 引入 Express 模塊
const express = require('express');
// 創(chuàng)建 Express 應(yīng)用實例
const app = express();
// 定義端口
const PORT = 3000;
// 定義根路由:處理 GET 請求
app.get('/', (req, res) => {
res.send('Hello Express!');
});
// 啟動服務(wù)器監(jiān)聽端口
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});核心解析:
express():創(chuàng)建一個 Express 應(yīng)用實例,封裝了 HTTP 服務(wù)器的創(chuàng)建和請求處理邏輯。app.get(path, handler):定義路由,監(jiān)聽path路徑的 GET 請求,handler是請求處理函數(shù),接收req(請求對象)和res(響應(yīng)對象)。res.send():發(fā)送響應(yīng)內(nèi)容,自動設(shè)置Content-Type(字符串默認(rèn)text/html,對象默認(rèn)application/json)。
2. 路由(Routing):請求的“導(dǎo)航系統(tǒng)”
路由是 Express 處理請求的核心,負(fù)責(zé)將不同的 HTTP 請求(方法+路徑)映射到對應(yīng)的處理函數(shù)。
(1)基本路由:支持所有 HTTP 方法
// GET 請求:獲取數(shù)據(jù)
app.get('/users', (req, res) => {
res.json({ users: ['Alice', 'Bob'] });
});
// POST 請求:創(chuàng)建數(shù)據(jù)
app.post('/users', (req, res) => {
res.status(201).json({ message: 'User created' });
});
// PUT 請求:更新數(shù)據(jù)(完整替換)
app.put('/users/:id', (req, res) => {
res.json({ message: `User ${req.params.id} updated` });
});
// DELETE 請求:刪除數(shù)據(jù)
app.delete('/users/:id', (req, res) => {
res.json({ message: `User ${req.params.id} deleted` });
});(2)路由參數(shù)與查詢字符串
- 路由參數(shù):通過
:name定義,存儲在req.params中(如/users/:id匹配/users/123,req.params.id = '123')。 - 查詢字符串:URL 中
?后的參數(shù),存儲在req.query中(如/users?page=1&limit=10,req.query = { page: '1', limit: '10' })。
app.get('/users/:id/posts', (req, res) => {
const { id } = req.params;
const { page = 1, limit = 10 } = req.query;
res.json({ userId: id, page, limit });
});
(3)路由模塊化:用express.Router()拆分代碼
當(dāng)路由增多時,將所有路由寫在一個文件中會導(dǎo)致代碼臃腫。使用 Router 可以將路由按模塊拆分:
// routes/users.js:用戶模塊路由
const express = require('express');
const router = express.Router();
// 注意:路徑是相對于掛載點的(這里的 '/' 對應(yīng) '/users')
router.get('/', (req, res) => res.json({ users: [] }));
router.get('/:id', (req, res) => res.json({ user: req.params.id }));
module.exports = router;// app.js:主文件中掛載路由
const userRouter = require('./routes/users');
// 將 userRouter 掛載到 '/users' 路徑下
app.use('/users', userRouter);
(4)路由鏈:簡化同一資源的多個路由
對于同一資源(如 /users)的多個 HTTP 方法,可以用 app.route() 鏈?zhǔn)秸{(diào)用:
app.route('/users')
.get((req, res) => res.json({ users: [] }))
.post((req, res) => res.status(201).json({ message: 'Created' }));
3. 中間件(Middleware):Express 的“靈魂”
中間件是 Express 最核心的設(shè)計理念——它是一個函數(shù),在請求-響應(yīng)周期中被調(diào)用,可以訪問 req、res 和 next(下一個中間件函數(shù))。中間件可以執(zhí)行任何代碼、修改請求/響應(yīng)對象、結(jié)束請求-響應(yīng)周期,或調(diào)用下一個中間件。
(1)中間件的分類與用法
- 應(yīng)用級中間件:綁定到
app實例,對所有請求生效(或指定路徑生效)。 - 路由級中間件:綁定到
express.Router()實例,僅對該路由模塊生效。 - 錯誤處理中間件:專門處理錯誤,有 4 個參數(shù)
(err, req, res, next)。 - 內(nèi)置中間件:Express 自帶的中間件(如
express.json、express.static)。 - 第三方中間件:社區(qū)開發(fā)的中間件(如
morgan、cors)。
(2)內(nèi)置中間件:最常用的兩個
express.json():解析請求體中的 JSON 數(shù)據(jù)(存儲到req.body)。express.urlencoded():解析 URL 編碼的請求體(如表單提交,存儲到req.body)。express.static():托管靜態(tài)文件(如 HTML、CSS、圖片)。
// 解析 JSON 請求體(必須放在路由之前)
app.use(express.json());
// 解析 URL 編碼請求體(extended: false 表示使用 querystring 庫解析)
app.use(express.urlencoded({ extended: false }));
// 托管 public 目錄下的靜態(tài)文件(訪問 /css/style.css 對應(yīng) public/css/style.css)
app.use(express.static('public'));
(3)自定義應(yīng)用級中間件
// 全局中間件:每個請求都會執(zhí)行
app.use((req, res, next) => {
console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
next(); // 調(diào)用下一個中間件,否則請求會掛起
});
// 路徑特定中間件:僅對 /api 路徑下的請求生效
app.use('/api', (req, res, next) => {
console.log('Accessing API');
next();
});(4)錯誤處理中間件
錯誤處理中間件必須有 4 個參數(shù),且要放在所有中間件和路由之后:
// 模擬一個錯誤路由
app.get('/error', (req, res, next) => {
const error = new Error('Something went wrong!');
error.status = 500;
next(error); // 將錯誤傳遞給錯誤處理中間件
});
// 錯誤處理中間件
app.use((err, req, res, next) => {
const status = err.status || 500;
const message = err.message || 'Internal Server Error';
res.status(status).json({ error: message });
});二、進階篇:構(gòu)建生產(chǎn)級應(yīng)用
1. 統(tǒng)一錯誤處理:優(yōu)雅處理同步與異步錯誤
Express 4.x 默認(rèn)不會捕獲異步代碼中的錯誤(如 async/await 中的 throw),需要手動包裝或使用工具。
(1)自定義錯誤類
先定義一個統(tǒng)一的錯誤類,方便傳遞狀態(tài)碼和錯誤信息:
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.isOperational = true; // 標(biāo)記為可預(yù)期的業(yè)務(wù)錯誤(非系統(tǒng)錯誤)
Error.captureStackTrace(this, this.constructor);
}
}
(2)異步錯誤包裝器
用一個高階函數(shù)包裝異步路由,自動捕獲錯誤并傳遞給錯誤處理中間件:
const asyncWrapper = (fn) => {
return (req, res, next) => {
fn(req, res, next).catch(next); // 捕獲異步錯誤,傳遞給 next(err)
};
};
(3)完整示例
// 路由中使用
app.get('/users/:id', asyncWrapper(async (req, res, next) => {
const user = await User.findById(req.params.id); // 模擬異步數(shù)據(jù)庫查詢
if (!user) {
throw new AppError('User not found', 404); // 拋出業(yè)務(wù)錯誤
}
res.json({ user });
}));
// 錯誤處理中間件(區(qū)分業(yè)務(wù)錯誤和系統(tǒng)錯誤)
app.use((err, req, res, next) => {
const { statusCode = 500, message, isOperational } = err;
res.status(statusCode).json({
error: isOperational ? message : 'Internal Server Error',
...(process.env.NODE_ENV === 'development' && { stack: err.stack }) // 開發(fā)環(huán)境返回堆棧
});
});2. RESTful API 設(shè)計實踐
Express 非常適合構(gòu)建 RESTful API,核心原則是“資源導(dǎo)向”,用 HTTP 方法表示操作類型。
(1)資源命名與 HTTP 方法對應(yīng)
| 資源 | GET(獲取) | POST(創(chuàng)建) | PUT(完整更新) | PATCH(部分更新) | DELETE(刪除) |
|---|---|---|---|---|---|
| /users | 獲取所有用戶 | 創(chuàng)建新用戶 | 批量更新用戶(不推薦) | 批量部分更新(不推薦) | 刪除所有用戶(不推薦) |
| /users/:id | 獲取指定 ID 的用戶 | 不允許 | 替換指定 ID 的用戶 | 部分更新指定 ID 的用戶 | 刪除指定 ID 的用戶 |
(2)示例:用戶管理 API
// 模擬用戶數(shù)據(jù)
let users = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' }
];
// 獲取所有用戶(支持分頁)
app.get('/users', asyncWrapper(async (req, res) => {
const { page = 1, limit = 10 } = req.query;
const start = (page - 1) * limit;
const end = start + parseInt(limit);
const paginatedUsers = users.slice(start, end);
res.json({
data: paginatedUsers,
page: parseInt(page),
limit: parseInt(limit),
total: users.length
});
}));
// 創(chuàng)建用戶
app.post('/users', asyncWrapper(async (req, res, next) => {
const { name, email } = req.body;
if (!name || !email) {
throw new AppError('Name and email are required', 400);
}
const newUser = { id: users.length + 1, name, email };
users.push(newUser);
res.status(201).json({ data: newUser });
}));3. 文件上傳:Multer 中間件
Express 本身不支持文件上傳,需要使用第三方中間件 multer。
(1)安裝與基本配置
npm install multer
const multer = require('multer');
// 配置存儲:指定文件存儲路徑和文件名
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/'); // 存儲到 uploads 目錄(需提前創(chuàng)建)
},
filename: (req, file, cb) => {
// 生成唯一文件名:時間戳 + 原始文件名
const uniqueName = `${Date.now()}-${file.originalname}`;
cb(null, uniqueName);
}
});
// 配置文件過濾器:只允許圖片上傳
const fileFilter = (req, file, cb) => {
if (file.mimetype.startsWith('image/')) {
cb(null, true); // 允許上傳
} else {
cb(new AppError('Only images are allowed', 400), false); // 拒絕上傳
}
};
// 初始化 Multer
const upload = multer({
storage,
fileFilter,
limits: { fileSize: 5 * 1024 * 1024 } // 限制文件大小 5MB
});(2)單文件與多文件上傳
// 單文件上傳:字段名為 avatar
app.post('/upload/avatar', upload.single('avatar'), (req, res) => {
res.json({
message: 'Avatar uploaded successfully',
file: req.file // 上傳的文件信息(路徑、大小等)
});
});
// 多文件上傳:字段名為 photos,最多 5 個
app.post('/upload/photos', upload.array('photos', 5), (req, res) => {
res.json({
message: 'Photos uploaded successfully',
files: req.files // 上傳的文件數(shù)組
});
});4. 安全最佳實踐
生產(chǎn)環(huán)境中必須關(guān)注安全,以下是 Express 應(yīng)用的核心安全措施:
(1)Helmet:設(shè)置安全 HTTP 頭
helmet 是一個安全中間件,通過設(shè)置各種 HTTP 頭來防止常見的 Web 攻擊(如 XSS、點擊劫持)。
npm install helmet
const helmet = require('helmet');
app.use(helmet()); // 啟用所有默認(rèn)安全頭
(2)CORS:配置跨域資源共享
如果 API 被前端跨域調(diào)用,需要配置 cors 中間件。
npm install cors
const cors = require('cors');
// 允許所有來源(開發(fā)環(huán)境用)
app.use(cors());
// 或僅允許特定來源(生產(chǎn)環(huán)境用)
app.use(cors({
origin: 'https://your-frontend.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
(3)速率限制:防止暴力攻擊
用 express-rate-limit 限制同一 IP 的請求頻率。
npm install express-rate-limit
const rateLimit = require('express-rate-limit');
// 全局速率限制:15 分鐘內(nèi)最多 100 個請求
const globalLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
message: 'Too many requests, please try again later'
});
app.use(globalLimiter);
// 登錄接口單獨限制:15 分鐘內(nèi)最多 5 個請求
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5,
message: 'Too many login attempts, please try again later'
});
app.post('/login', loginLimiter, (req, res) => {
res.json({ message: 'Login successful' });
});(4)輸入驗證:防止惡意數(shù)據(jù)
用 joi 或 express-validator 驗證請求參數(shù)和請求體。
npm install joi
const Joi = require('joi');
// 定義用戶創(chuàng)建的驗證 schema
const userSchema = Joi.object({
name: Joi.string().min(2).max(30).required(),
email: Joi.string().email().required(),
age: Joi.number().integer().min(18).max(100)
});
// 在路由中使用
app.post('/users', (req, res, next) => {
const { error } = userSchema.validate(req.body);
if (error) {
throw new AppError(error.details[0].message, 400);
}
next(); // 驗證通過,繼續(xù)處理
}, (req, res) => {
res.json({ message: 'User created' });
});5. 性能優(yōu)化
生產(chǎn)環(huán)境中需要關(guān)注性能,以下是幾個核心優(yōu)化點:
(1)壓縮響應(yīng):使用compression中間件
npm install compression
const compression = require('compression');
app.use(compression()); // 壓縮所有響應(yīng)(放在路由之前)
(2)靜態(tài)文件緩存
給靜態(tài)文件設(shè)置 maxAge,讓瀏覽器緩存:
app.use(express.static('public', {
maxAge: '1d' // 緩存 1 天
}));
(3)避免同步操作
所有 I/O 操作(數(shù)據(jù)庫、文件系統(tǒng))都使用異步方式,避免阻塞事件循環(huán)。
到此這篇關(guān)于node.js 中的 express 框架基礎(chǔ)到進階指南的文章就介紹到這了,更多相關(guān)node.js express 框架內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
利用Node轉(zhuǎn)換Excel成JSON的詳細(xì)步驟
最近工作中遇到一個需求,大致需求就是將Excel文件在導(dǎo)入時解析為json格式轉(zhuǎn)換數(shù)據(jù)結(jié)構(gòu)再傳輸給后臺,下面這篇文章主要給大家介紹了關(guān)于如何利用Node轉(zhuǎn)換Excel成JSON的詳細(xì)步驟,需要的朋友可以參考下2022-11-11
node.js中使用ejs渲染數(shù)據(jù)的代碼實現(xiàn)
這篇文章主要介紹了node.js中使用ejs渲染數(shù)據(jù),本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2023-11-11
詳解本地Node.js服務(wù)器作為api服務(wù)器的解決辦法
本篇文章主要介紹了詳解本地Node.js服務(wù)器作為api服務(wù)器的解決辦法,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-02-02

