Express.js框架
介绍
Express.js是Node.js最流行的Web应用框架之一,由TJ Holowaychuk于2010年创建。它提供了简洁而强大的API,帮助开发者快速构建Web应用和RESTful API。Express.js基于Node.js的HTTP模块,添加了路由、中间件、错误处理等功能,使Web开发更加高效。其核心 philosophy是"少即是多",提供基础功能而不限制开发者的选择。
核心特点
- 轻量级:仅提供必要的功能,无冗余代码
- 灵活:不强制使用特定的模板引擎或ORM
- 高性能:基于Node.js的非阻塞I/O模型
- 丰富的中间件生态:通过中间件扩展功能
- 路由系统:简洁而强大的路由API
- 社区活跃:拥有庞大的社区和丰富的资源
原理
中间件机制
Express.js的核心是中间件(Middleware)机制:
- 中间件是一个函数,可以访问请求对象(req)、响应对象(res)和应用的请求-响应循环中的下一个中间件函数(next)
- 中间件可以执行任何操作,修改请求和响应对象,结束请求-响应循环,或调用下一个中间件
- 中间件按照定义的顺序执行
中间件类型
-
应用级中间件:绑定到app对象的中间件
app.use((req, res, next) => {
console.log('Time:', Date.now());
next();
}); -
路由级中间件:绑定到路由对象的中间件
router.use((req, res, next) => {
console.log('Request URL:', req.originalUrl);
next();
}); -
错误处理中间件:处理请求过程中发生的错误
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
}); -
内置中间件:Express.js提供的内置中间件
app.use(express.json()); // 解析JSON请求体
app.use(express.urlencoded({ extended: true })); // 解析URL编码的请求体
app.use(express.static('public')); // 提供静态文件 -
第三方中间件:通过NPM安装的中间件
const cookieParser = require('cookie-parser');
app.use(cookieParser());
路由原理
Express.js的路由系统基于路径和HTTP方法进行匹配:
- 当请求到达服务器时,Express会根据请求的URL和方法查找匹配的路由处理函数
- 路由可以包含路径参数、查询字符串等
- 路由处理函数负责生成响应
路由匹配规则
- 精确路径匹配:
app.get('/users', ...)匹配/users - 路径参数:
app.get('/users/:id', ...)匹配/users/123 - 通配符:
app.get('/users/*', ...)匹配/users/下的所有路径 - 正则表达式:
app.get(/^/users/d+$/, ...)匹配/users/后跟随数字的路径
图示
请求 -> 应用级中间件 -> 路由级中间件 -> 路由处理函数 -> 响应
| |
v v
错误处理中间件 <- 错误处理中间件
核心对象
请求对象(req)
请求对象包含HTTP请求的所有信息:
req.url:请求URLreq.method:请求方法req.headers:请求头req.body:请求体(需要body-parser中间件)req.params:路径参数req.query:查询字符串参数req.cookies:Cookie(需要cookie-parser中间件)
响应对象(res)
响应对象用于发送HTTP响应:
res.send():发送响应res.json():发送JSON响应res.status():设置HTTP状态码res.setHeader():设置响应头res.redirect():重定向res.render():渲染模板(需要模板引擎)
实例
基本使用
// app.js
const express = require('express');
const app = express();
const port = 3000;
// 应用级中间件
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// 路由定义
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.get('/users', (req, res) => {
// 模拟用户数据
const users = [
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Smith' }
];
res.json(users);
});
app.get('/users/:id', (req, res) => {
const userId = req.params.id;
// 模拟根据ID查询用户
res.json({ id: userId, name: `User ${userId}` });
});
app.post('/users', (req, res) => {
const newUser = req.body;
// 模拟创建用户
res.status(201).json({ id: 3, ...newUser });
});
// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
// 启动服务器
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
路由模块化
// routes/users.js
const express = require('express');
const router = express.Router();
// 路由级中间件
router.use((req, res, next) => {
console.log('Time:', Date.now());
next();
});
router.get('/', (req, res) => {
res.json([{ id: 1, name: 'John Doe' }]);
});
router.get('/:id', (req, res) => {
res.json({ id: req.params.id, name: `User ${req.params.id}` });
});
module.exports = router;
// app.js
const usersRouter = require('./routes/users');
app.use('/users', usersRouter);
最佳实践
- 使用环境变量:存储配置信息,如端口号、数据库连接字符串等
- 模块化路由:将路由拆分为多个文件,提高可维护性
- 错误处理:使用错误处理中间件捕获和处理所有错误
- 日志记录:记录请求和响应信息,便于调试和监控
- 输入验证:验证所有用户输入,防止注入攻击
- 使用async/await:避免回调地狱
- 限制请求速率:防止DoS攻击
- 设置安全头部:使用helmet中间件增强安全性
- 代码风格一致:使用ESLint和Prettier保持代码风格一致
- 测试:编写单元测试和集成测试
// 中间件
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// 路由
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.get('/users/:id', (req, res) => {
res.json({
id: req.params.id,
name: 'User ' + req.params.id
});
});
// 启动服务器
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}/`);
});
运行:`node app.js`
访问:http://localhost:3000/ 可看到'Hello World'
访问:http://localhost:3000/users/1 可看到'{"id":"1","name":"User 1"}'
### 中间件示例
```javascript
// 日志中间件
const logger = (req, res, next) => {
console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
next();
};
// 错误处理中间件
const errorHandler = (err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
};
app.use(logger);
// 其他路由...
app.use(errorHandler);
专业解决方案
路由模块化
- 使用
express.Router()创建模块化路由 - 将不同功能的路由分离到不同文件中
身份验证
- passport.js:灵活的身份验证中间件
- jsonwebtoken:基于JWT的身份验证
数据库集成
- mongoose:MongoDB的ODM
- sequelize:ORM框架,支持多种关系型数据库
视图引擎
- ejs:简单的模板引擎
- pug:优雅的模板引擎
测试
- mocha:JavaScript测试框架
- supertest:HTTP断言库,用于测试Express应用
性能优化
- 静态文件缓存
- 压缩响应数据
- 路由懒加载
- 使用CDN分发静态资源