跳到主要内容

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)
  • 中间件可以执行任何操作,修改请求和响应对象,结束请求-响应循环,或调用下一个中间件
  • 中间件按照定义的顺序执行

中间件类型

  1. 应用级中间件:绑定到app对象的中间件

    app.use((req, res, next) => {
    console.log('Time:', Date.now());
    next();
    });
  2. 路由级中间件:绑定到路由对象的中间件

    router.use((req, res, next) => {
    console.log('Request URL:', req.originalUrl);
    next();
    });
  3. 错误处理中间件:处理请求过程中发生的错误

    app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).send('Something broke!');
    });
  4. 内置中间件:Express.js提供的内置中间件

    app.use(express.json()); // 解析JSON请求体
    app.use(express.urlencoded({ extended: true })); // 解析URL编码的请求体
    app.use(express.static('public')); // 提供静态文件
  5. 第三方中间件:通过NPM安装的中间件

    const cookieParser = require('cookie-parser');
    app.use(cookieParser());

路由原理

Express.js的路由系统基于路径和HTTP方法进行匹配:

  1. 当请求到达服务器时,Express会根据请求的URL和方法查找匹配的路由处理函数
  2. 路由可以包含路径参数、查询字符串等
  3. 路由处理函数负责生成响应

路由匹配规则

  • 精确路径匹配: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:请求URL
  • req.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);

最佳实践

  1. 使用环境变量:存储配置信息,如端口号、数据库连接字符串等
  2. 模块化路由:将路由拆分为多个文件,提高可维护性
  3. 错误处理:使用错误处理中间件捕获和处理所有错误
  4. 日志记录:记录请求和响应信息,便于调试和监控
  5. 输入验证:验证所有用户输入,防止注入攻击
  6. 使用async/await:避免回调地狱
  7. 限制请求速率:防止DoS攻击
  8. 设置安全头部:使用helmet中间件增强安全性
  9. 代码风格一致:使用ESLint和Prettier保持代码风格一致
  10. 测试:编写单元测试和集成测试
// 中间件
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分发静态资源