入坑node.js全面指南(二)

251 阅读5分钟

1. Express.js 架构哲学

作为 Node.js 生态中应用最广泛的 Web 框架,Express.js 以"最小化设计原则"为核心,通过模块化架构实现灵活扩展。

核心设计理念

  • 简约而不简单:仅提供 Web 开发的基础功能,其他能力通过中间件扩展
  • 中间件驱动架构:采用洋葱模型实现请求处理流水线
  • 无强制约束:不限定项目结构,开发者拥有完全自由
  • 渐进式增强:可根据需求添加功能模块,避免过度设计

关键架构组件

  • 中间件系统:请求处理流水线,每个中间件可访问请求/响应对象
  • 路由分发器:基于 HTTP 方法和 URL 的高效路由匹配
  • 视图引擎接口:支持多种模板引擎(EJS、Pug等)
  • 扩展接口:通过 app.set()/app.get() 管理应用配置

安装

npm install express

基本应用结构

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 Express!');
});

// 启动服务器
app.listen(port, () => {
  console.log(`服务器运行在 http://localhost:${port}`);
});

2. 中间件

中间件类型

  1. 应用级中间件:绑定到 app 实例

    // 记录请求时间的应用级中间件
    app.use((req, res, next) => {
      req.requestTime = Date.now();
      console.log(`请求时间: ${req.requestTime}`);
      next(); // 必须调用next传递控制权
    });
    
  2. 路由级中间件:绑定到路由实例

    const router = express.Router();
    router.use((req, res, next) => {
      console.log('路由中间件执行');
      next();
    });
    
  3. 错误处理中间件:四个参数的特殊中间件

    app.use((err, req, res, next) => {
      console.error(err.stack);
      res.status(500).send('服务器错误!');
    });
    
  4. 内置中间件

    app.use(express.json()); // 解析JSON请求体
    app.use(express.static('public')); // 静态文件服务
    
  5. 第三方中间件

    const helmet = require('helmet');
    app.use(helmet()); // 安全头部设置
    

中间件执行顺序

Express 中间件按照声明顺序执行,理解这一点对于正确配置应用至关重要:

  1. 请求到达服务器
  2. 按顺序执行匹配的中间件
  3. 每个中间件可以修改 req/res 对象
  4. 中间件通过 next() 传递控制权
  5. 当某个中间件发送响应时,链终止

3. 路由系统深度解析

路由基础

// 基础路由
app.get('/products', (req, res) => {
  res.json([{ id: 1, name: '手机' }]);
});

// 路由参数
app.get('/products/:id', (req, res) => {
  const id = req.params.id;
  res.json({ id, name: `商品${id}` });
});

// 查询参数
app.get('/search', (req, res) => {
  const q = req.query.q;
  res.send(`搜索: ${q}`);
});

4. 请求与响应处理

请求对象 (req)

  • req.params:路由参数
  • req.query:查询字符串参数
  • req.body:请求体内容(需要中间件解析)
  • req.cookies:客户端 cookies
  • req.headers:HTTP 请求头
  • req.ip:客户端 IP 地址

响应对象 (res)

  • res.status(code):设置状态码
  • res.send(body):发送响应
  • res.json(obj):发送 JSON 响应
  • res.render(view, locals):渲染模板
  • res.redirect(path):重定向
  • res.set(field, value):设置响应头
// 综合使用示例
app.post('/login', (req, res) => {
  const { username, password } = req.body;
  
  if (!username || !password) {
    return res.status(400).json({ error: '用户名和密码必填' });
  }
  
  // 认证逻辑...
  res.cookie('token', 'abc123', { httpOnly: true });
  res.redirect('/dashboard');
});

5. Express vs Koa vs Node 深度对比

5.1 Express 与 Node.js 的关系

关系解析

  • Node.js 是运行时环境,提供 HTTP 模块等基础能力
  • Express 是基于 Node.js HTTP 模块的封装框架
  • Express 简化了路由、中间件等常见 Web 开发任务

5.2 Express 与 Koa 的深度对比

  • 设计理念:Express.js 设计较为灵活,提供了丰富的功能和插件,适合快速开发各种类型的 Web 应用。而 Koa 是由 Express.js 的原班人马打造,它更注重简洁和优雅,采用了更现代化的异步编程方式(如 async/await),并且没有捆绑任何中间件,开发者可以根据需求自由选择和组合中间件。​

  • 中间件机制:Express.js 的中间件采用传统的回调函数形式,中间件之间通过next()方法传递控制权。在处理复杂的异步操作时,可能会出现回调地狱的问题。Koa 的中间件基于 async/await 语法,通过await next()实现中间件的执行和传递,代码更加简洁、易读,避免了回调地狱。​

  • 错误处理:在 Express.js 中,错误处理通常通过在中间件或路由处理函数中捕获错误,并传递给错误处理中间件。而 Koa 使用 try/catch 块或者在 async 函数中返回错误,通过onerror事件来统一处理错误,错误处理更加直观和方便。

Express vs Koa 核心差异

特性ExpressKoa
中间件模型线性链式调用洋葱模型
异步处理回调函数原生Async/Await
错误处理集中式错误中间件Try/Catch捕获
路由系统内置Router需koa-router
请求/响应封装原生对象扩展统一Context对象
体积大小约4.5MB约0.6MB

6. Express 实战案例:RESTful API 实现

const express = require('express');
const app = express();
app.use(express.json());

// 内存数据库
let users = [
  { id: 1, name: '张三', email: 'zhang@example.com' },
  { id: 2, name: '李四', email: 'li@example.com' }
];

// 获取所有用户
app.get('/api/users', (req, res) => {
  res.json({
    status: 'success',
    count: users.length,
    data: users
  });
});

// 创建新用户
app.post('/api/users', (req, res) => {
  const { name, email } = req.body;
  const newUser = {
    id: users.length + 1,
    name,
    email
  };
  
  users.push(newUser);
  
  res.status(201).json({
    status: 'success',
    data: newUser
  });
});

});

// 全局404处理
app.use((req, res) => {
  res.status(404).json({
    status: 'error',
    message: '路由不存在'
  });
});

// 错误处理中间件
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({
    status: 'error',
    message: '服务器内部错误'
  });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`服务器运行在 http://localhost:${PORT}`);
});

7. 总结

Express.js的优势:

  1. 简洁核心+丰富扩展:保持核心轻量,通过中间件提供无限扩展
  2. 渐进式采用路径:从简单路由到复杂应用均可胜任
  3. 社区驱动生态:庞大的中间件生态系统解决各种场景需求
  4. 文档与稳定性:优秀的文档质量和稳定的API设计

最佳实践建议

  1. 使用 express.Router 实现路由模块化
  2. 中间件按功能拆分为独立模块
  3. 统一错误处理机制
  4. 使用 helmet 增强安全性
  5. 结合 morgan 记录访问日志
  6. 使用 express-validator 进行输入验证