使用 Express.js 构建高效的 Node.js Web 应用
Express.js 简介
Node.js 提供了构建服务器的基础能力,但直接使用原生模块处理 HTTP 请求、路由和响应会显得繁琐且代码量大。Express.js 是一个极简且灵活的 Node.js Web 应用框架,它极大地简化了这些开发流程,使服务器开发更加高效和优雅。
可以将 Express.js 想象成一个高效的工具箱,而原生 Node.js 则是提供基础材料。使用 Express.js,我们可以更快地搭建出功能完善的 Web 应用。
原生 Node.js 与 Express.js 对比
1. 原生 Node.js HTTP 服务器示例(部分代码)
const http = require('http');
const server = http.createServer((req, res) => {
const { url, method } = req;
if (url === '/' && method === 'GET') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>首页</h1>');
} else if (url === '/about' && method === 'GET') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>关于页面</h1>');
} else {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('<h1>404 - 未找到</h1>');
}
});
server.listen(3000, () => {
console.log('服务器运行在 3000 端口');
});
原生 Node.js 的主要挑战包括:手动解析 URL 和请求体、大量的条件判断来处理路由和方法,以及重复的响应头设置。
2. 使用 Express.js 的等效示例
const express = require('express');
const app = express();
// 自动解析 JSON 请求体
app.use(express.json());
// 定义路由
app.get('/', (req, res) => {
res.send('<h1>首页</h1>');
});
app.get('/about', (req, res) => {
res.send('<h1>关于页面</h1>');
});
// 定义 POST 路由,自动处理请求体
app.post('/api/data', (req, res) => {
const receivedData = req.body;
res.status(201).json({ message: '数据接收成功', data: receivedData });
});
app.listen(3000, () => {
console.log('Express 服务器运行在 3000 端口');
});
Express.js 提供了声明式的路由定义、自动化的请求体解析(如 express.json() 和 express.urlencoded()),以及简洁的响应方法(如 res.send() 和 res.json()),显著提高了开发效率和代码可读性。
创建你的第一个 Express 服务器
步骤 1: 初始化项目
# 创建项目目录
mkdir my-express-app
cd my-express-app
# 初始化 npm 项目
npm init -y
# 安装 Express
npm install express
步骤 2: 编写服务器代码 (server.js)
// server.js
const express = require('express');
const app = express();
const PORT = 3000;
// 定义一个简单的 GET 路由
app.get('/', (req, res) => {
res.send('你好,Express!');
});
// 启动服务器
app.listen(PORT, () => {
console.log(`服务器正在监听 http://localhost:${PORT}`);
});
步骤 3: 运行服务器
node server.js
Express 应用对象 (app) 的核心功能
app 对象是 Express 应用的入口,提供了处理 HTTP 请求的方法:
app.get(path, handler): 处理 GET 请求。app.post(path, handler): 处理 POST 请求。app.put(path, handler): 处理 PUT 请求。app.delete(path, handler): 处理 DELETE 请求。app.use(middleware): 挂载中间件。
处理不同的 HTTP 请求
GET 请求
GET 请求通常用于获取资源。
1. 基本 GET 路由
app.get('/greeting', (req, res) => {
res.send('来自服务器的问候!');
});
// 访问: http://localhost:3000/greeting
2. 带有路由参数的 GET 请求
路由参数用于捕获 URL 中的动态部分。
// :userId 是一个路由参数
app.get('/users/:userId', (req, res) => {
const userId = req.params.userId;
res.send(`正在查看用户ID: ${userId} 的资料`);
});
// 访问: http://localhost:3000/users/123
// 输出: "正在查看用户ID: 123 的资料"
3. 带有查询字符串的 GET 请求
查询字符串(Query String)通常在 ? 之后,用于传递额外的参数。
app.get('/search', (req, res) => {
const { query, limit } = req.query;
res.send(`搜索关键词: "${query}", 限制数量: ${limit}`);
});
// 访问: http://localhost:3000/search?query=nodejs&limit=20
// 输出: "搜索关键词: "nodejs", 限制数量: 20"
POST 请求与请求体解析
POST 请求常用于提交数据。
1. 配置请求体解析中间件
在定义路由之前,需要使用中间件来解析请求体。
// 解析 JSON 格式的请求体
app.use(express.json());
// 解析 URL 编码格式的请求体 (常用于 HTML 表单提交)
app.use(express.urlencoded({ extended: true }));
2. 基本 POST 路由
app.post('/api/messages', (req, res) => {
const { text, sender } = req.body;
console.log(`收到来自 ${sender} 的消息: ${text}`);
res.status(201).send(`消息已成功接收`);
});
使用 express.Router 组织路由
当应用变得复杂时,将所有路由放在一个文件中会难以维护。express.Router 允许你将相关的路由模块化。
1. 创建路由模块 (routes/products.js)
// routes/products.js
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.json([{ id: 1, name: 'Laptop' }, { id: 2, name: 'Keyboard' }]);
});
router.post('/', (req, res) => {
const newProduct = req.body;
res.status(201).json(newProduct);
});
module.exports = router;
2. 在主服务器文件中挂载路由模块 (server.js)
// server.js
// ... (其他 Express 配置)
const productRoutes = require('./routes/products');
// 将 productRoutes 挂载到 /api/products 路径下
app.use('/api/products', productRoutes);
// ... (服务器启动代码)
现在,访问 /api/products 相关的请求将由 routes/products.js 文件中的路由处理。
路由顺序的重要性
Express 按顺序匹配路由。因此,应该将更具体的路由放在前面,将更通用的路由(如带参数的路由)放在后面。
// ✅ 正确的顺序:先匹配具体路径
app.get('/users/profile', (req, res) => {
res.send('用户个人资料');
});
// ✅ 然后匹配带参数的路径
app.get('/users/:userId', (req, res) => {
res.send(`用户ID: ${req.params.userId}`);
});
// ❌ 错误的顺序:带参数的路由会"捕获"所有 /users/* 的请求,导致 /users/profile 永远无法匹配
// app.get('/users/:userId', ...);
// app.get('/users/profile', ...); // 这行永远不会被执行
总结
Express.js 通过提供简洁的 API 和强大的功能集,极大地简化了 Node.js Web 应用的开发。它使得路由定义、请求处理和模块化组织变得更加直观和高效,是构建现代 Node.js Web 服务的理想选择。