跳到主要内容

Express路由系统详解

1. 路由基础回顾

路由是指应用程序如何响应客户端对特定端点的请求,这些端点由URI(或路径)和HTTP请求方法(GET、POST等)定义。在Express中,路由的基本结构如下:

app.METHOD(PATH, HANDLER)

其中:

  • app 是Express实例
  • METHOD 是HTTP请求方法
  • PATH 是服务器上的路径
  • HANDLER 是当路由匹配时执行的函数

2. 路由方法

Express支持所有HTTP请求方法,最常用的包括:

2.1 GET路由

用于获取资源:

app.get('/users', (req, res) => {
res.send('获取用户列表')
})

2.2 POST路由

用于创建新资源:

app.post('/users', (req, res) => {
res.send('创建新用户')
})

2.3 PUT路由

用于更新现有资源:

app.put('/users/:id', (req, res) => {
res.send(`更新ID为${req.params.id}的用户`)
})

2.4 DELETE路由

用于删除资源:

app.delete('/users/:id', (req, res) => {
res.send(`删除ID为${req.params.id}的用户`)
})

2.5 特殊路由方法

  • app.all(): 匹配所有HTTP方法
  • app.use(): 用于中间件,通常不指定特定路径

3. 路由路径

路由路径与请求方法结合,定义了可以访问应用程序的端点。Express支持多种路由路径定义方式:

3.1 字符串路径

app.get('/about', (req, res) => {
res.send('关于我们')
})

3.2 字符串模式路径

使用特殊字符定义匹配模式:

// 匹配 acd 和 abcd
app.get('/ab?cd', (req, res) => {
res.send('ab?cd')
})

// 匹配 abcd, abbcd, abbbcd 等
app.get('/ab+cd', (req, res) => {
res.send('ab+cd')
})

// 匹配 abcd, abxcd, abRABDOMcd, ab123cd 等
app.get('/ab*cd', (req, res) => {
res.send('ab*cd')
})

3.3 正则表达式路径

使用正则表达式进行更复杂的匹配:

// 匹配任何包含 a 的路径
app.get(/a/, (req, res) => {
res.send('/a/')
})

// 匹配 butterfly 和 dragonfly,但不匹配 butterflyman, dragonflyman 等
app.get(/.*fly$/, (req, res) => {
res.send('/.*fly$/')
})

4. 路由参数

路由参数是命名的URL段,用于捕获URL中指定位置的值。捕获的值填充到req.params对象中:

app.get('/users/:userId/books/:bookId', (req, res) => {
res.send(req.params)
// 对于 /users/34/books/8989,输出 { "userId": "34", "bookId": "8989" }
})

路由参数的名称必须由"单词字符"([A-Za-z0-9_])组成。

5. 路由处理程序

路由可以有多个回调函数,它们的行为类似于中间件,但有一个关键区别:这些回调函数可能会调用next('route')跳过其余的路由回调。

5.1 多个回调函数

app.get('/example/b', (req, res, next) => {
console.log('响应将由下一个函数发送')
next()
}, (req, res) => {
res.send('Hello from B!')
})

5.2 回调函数数组

const cb0 = (req, res, next) => {
console.log('CB0')
next()
}

const cb1 = (req, res, next) => {
console.log('CB1')
next()
}

const cb2 = (req, res) => {
res.send('Hello from C!')
}

app.get('/example/c', [cb0, cb1, cb2])

5.3 混合使用函数和数组

app.get('/example/d', [cb0, cb1], (req, res, next) => {
console.log('路由处理程序的剩余部分')
next()
}, (req, res) => {
res.send('Hello from D!')
})

6. 路由器

Express提供了一个Router对象,可以看作是一个"迷你应用程序",它可以执行中间件和路由功能,但不能独立启动服务器。

6.1 创建路由器

const express = require('express')
const router = express.Router()

6.2 在路由器上定义路由

// 中间件特定于该路由器
router.use((req, res, next) => {
console.log('Time: ', Date.now())
next()
})

// 定义根路由
router.get('/', (req, res) => {
res.send('Birds home page')
})

// 定义 about 路由
router.get('/about', (req, res) => {
res.send('About birds')
})

6.3 将路由器挂载到应用程序

app.use('/birds', router)

现在,应用程序将能够处理像 /birds//birds/about 这样的请求。

7. 路由模块化

将相关路由组织到单独的文件中是一种良好的实践,这有助于保持代码的可维护性。

7.1 创建路由模块文件

创建 routes/users.js

const express = require('express')
const router = express.Router()

// 获取所有用户
router.get('/', (req, res) => {
res.send('获取所有用户')
})

// 创建新用户
router.post('/', (req, res) => {
res.send('创建新用户')
})

// 获取单个用户
router.get('/:id', (req, res) => {
res.send(`获取ID为${req.params.id}的用户`)
})

module.exports = router

7.2 在主应用程序中使用路由模块

const express = require('express')
const app = express()
const usersRouter = require('./routes/users')

app.use('/users', usersRouter)

8. 高级路由技巧

8.1 链式路由处理

Express允许链式调用路由方法,这对于简化代码很有用:

app.route('/book')
.get((req, res) => {
res.send('获取图书列表')
})
.post((req, res) => {
res.send('创建新书')
})
.put((req, res) => {
res.send('更新书')
})

8.2 路由前缀

为一组路由添加统一的前缀:

// 所有以 /api 开头的请求都会使用这个路由器
app.use('/api', apiRouter)

8.3 路由中间件组合

为路由组应用特定的中间件:

// 为所有用户路由应用身份验证中间件
app.use('/users', authMiddleware, usersRouter)

9. 路由最佳实践

  1. 模块化路由:将相关路由组织到单独的文件中
  2. 使用路由器:利用Express Router创建可重用的路由处理程序
  3. 遵循RESTful约定:使用适当的HTTP方法表示操作类型
  4. 验证输入:始终验证和清理用户输入
  5. 错误处理:实现合理的错误处理逻辑
  6. 文档化路由:使用工具如Swagger记录API路由

10. 练习项目

创建一个模块化的Express应用程序,包含以下功能:

  1. 用户管理路由模块
  2. 产品管理路由模块
  3. 订单管理路由模块
  4. 适当的中间件应用

通过这个练习,你将掌握Express路由系统的高级用法,为构建复杂的Web应用程序打下坚实的基础。