Express 路由

预计阅读时间3 分钟 157 views

Express路由是指定如何响应客户端请求的方式。你可以使用Express应用程序对象的方法来定义路由,这些方法对应于HTTP方法,比如使用app.get()处理GET请求,app.post()处理POST请求等等。你也可以使用app.all()来处理所有HTTP方法,以及使用app.use()来指定中间件作为回调函数。

这些路由方法指定了一个回调函数,当应用程序收到指定路由和HTTP方法的请求时就会调用它。换句话说,应用程序会监听与指定路由和方法匹配的请求,一旦匹配到,就会调用指定的回调函数。

实际上,路由方法可以有多个回调函数作为参数。在有多个回调函数的情况下,你需要向回调函数提供next作为参数,然后在函数体内部调用next()将控制权交给下一个回调函数。

下面的代码是一个非常基础的路由示例:

// 引入 Express 模块
const express = require('express');

// 创建 Express 应用程序实例
const app = express();

// 当向主页发出 GET 请求时,以 "hello world "作为响应
app.get('/', (req, res) => {
  res.send('hello world');
});

路由方法

路由方法是从HTTP方法中派生出来的,并附加到 Express 类的一个实例上。

以下代码是针对应用程序根目录定义的GET和POST方法的示例:

// GET 方法路由
app.get('/', (req, res) => {
  res.send('GET 请求到达首页')
})

// POST 方法路由
app.post('/', (req, res) => {
  res.send('POST 请求到达首页')
})

Express支持与所有HTTP请求方法对应的方法:get、post等等。还有一个特殊的路由方法 app.all(),用于在路径上为所有 HTTP 请求方法加载中间件函数。例如,以下处理程序用于处理对路径”/secret”的所有请求,无论使用GET、POST、PUT、DELETE还是 http 模块中支持的其他任何HTTP请求方法:

app.all('/secret', (req, res, next) => {
  console.log('Accessing the secret section ...')
  next() // 将控制权传递给下一个处理程序
})

路由路径

路由路径是与请求方法结合在一起的,它定义了可以接收请求的端点。路由路径可以是字符串、字符串模式或正则表达式。

在路由路径中,字符 ?, +, *, 和 () 是正则表达式的一部分。连字符 (-) 和点号 (.) 在基于字符串的路径中直接解释。

如果要在路径字符串中使用美元符号 ($),需要将其转义并用([ 和 ])包围起来。例如,对于路径 “/data/$book” 的请求,路径字符串将是 “/data/([$])book”。

查询字符串不是路由路径的一部分。

以下是一些基于字符串的路由路径示例:

// 这个路由路径匹配根路由,即 /
app.get('/', (req, res) => {
  res.send('root');
});

// 这个路由路径匹配 /about
app.get('/about', (req, res) => {
  res.send('about');
});

// 这个路由路径匹配 /random.text
app.get('/random.text', (req, res) => {
  res.send('random.text');
});

以下是一些基于字符串模式的路由路径示例:

// 这个路由路径匹配 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, abRANDOMcd, ab123cd, 等等
app.get('/ab*cd', (req, res) => {
  res.send('ab*cd');
});

// 这个路由路径匹配 /abe 和 /abcde
app.get('/ab(cd)?e', (req, res) => {
  res.send('ab(cd)?e');
});

以下是一些基于正则表达式的路由路径示例:

// 这个路由路径匹配带有“a”字符的任何内容
app.get(/a/, (req, res) => {
  res.send('/a/');
});

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

通过理解这些概念,开发人员可以有效地在 Express 应用程序中定义路由路径,处理各种 HTTP 请求。

路由参数

路由参数是 URL 中的命名部分,用于捕获在 URL 中指定位置的值。捕获的值会填充到 req.params 对象中,其中路由参数在路径中指定的名称作为它们各自的键。

比如说,有这样一个路由路径:/users/:userId/books/:bookId。当访问 http://localhost:3000/users/34/books/8989 这样的URL时,Express会自动将 userId 设置为 34bookId 设置为 8989,然后存储在 req.params 中,以便在处理请求时使用。

要定义带有路由参数的路由,只需在路径中指定参数的名称即可。例如:

app.get('/users/:userId/books/:bookId', (req, res) => {
  res.send(req.params);
});

路由参数的名称必须由字母、数字或下划线组成,且不能以数字开头。除了字母、数字和下划线外,还可以在参数名称中使用破折号(-)和点号(.)。

比如,如果有这样的路由路径:/flights/:from-:to,对应的请求URL是 http://localhost:3000/flights/LAX-SFO,那么 req.params 将会是 { "from": "LAX", "to": "SFO" }

如果想要更精确地控制参数的匹配规则,可以在参数名后面加上一个正则表达式。例如,/user/:userId(\d+) 可以匹配数字形式的用户ID,如请求URL http://localhost:3000/user/42req.params 将会是 { "userId": "42" }

需要注意的是,因为正则表达式通常是字面字符串的一部分,所以在使用时需要注意转义。如果想匹配一个或多个数字,正则表达式是 \\d+

在 Express 4.x 中,正则表达式中的 * 字符并不按照通常的方式解释。作为替代方案,可以使用 {0,} 来匹配任意数量的任意字符,这个问题在 Express 5 中可能会得到修复。

路由处理程序

路由处理程序就像是一系列函数,用于处理不同的请求。你可以给一个路由指定一个函数,也可以给它指定多个函数。这些函数会按顺序执行,但如果其中一个函数决定不再继续处理这个路由,它可以调用 next('route') 来跳过后面的函数。

首先,来看一个简单的例子。我们有一个路由 /example/a,且给它指定了一个函数,当有请求访问这个路由时,这个函数就会被调用,然后返回一条消息。

app.get('/example/a', (req, res) => {
  res.send('Hello from A!')
})

接下来,看一个稍微复杂一点的例子。在这个例子中,有一个路由 /example/b,并给它指定了两个函数。第一个函数会在请求到来时打印一条消息,然后调用 next(),将控制权传递给下一个函数。第二个函数接收到控制权后,会返回另一条消息。

app.get('/example/b', (req, res, next) => {
  console.log('the response will be sent by the next function ...')
  next()
}, (req, res) => {
  res.send('Hello from B!')
})

还有一种情况是,给一个路由指定一个函数数组。这个数组中的函数会按顺序执行,每个函数都可以决定是否继续处理这个路由。

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

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

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

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

最后,我们还可以将独立的函数和函数数组组合在一起来处理一个路由。这种方式可以更灵活地组织代码,以便处理不同的情况。

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

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

app.get('/example/d', [cb0, cb1], (req, res, next) => {
  console.log('the response will be sent by the next function ...')
  next()
}, (req, res) => {
  res.send('Hello from D!')
})

响应方法

响应对象(res)上的方法可以向客户端发送响应,并终止请求-响应循环。如果路由处理程序没有调用这些方法,客户端请求将被挂起。下面是一些常用的响应方法及其描述:

方法描述
res.download()提示用户下载文件。
res.end()结束响应过程,不再发送任何数据。
res.json()发送 JSON 格式的响应数据。
res.jsonp()发送支持 JSONP 的 JSON 格式的响应数据。
res.redirect()重定向到另一个 URL。
res.render()渲染视图模板并发送给客户端。
res.send()发送各种类型的响应数据,可以是字符串、对象、数组等。
res.sendFile()将文件以二进制流形式发送给客户端,通常用于发送文件。
res.sendStatus()设置响应的状态码,并将对应的状态信息发送给客户端。

app.route()

app.route() 方法可以让你为同一个路由路径创建多个处理函数,这些处理函数可以按顺序执行。这种方法有助于减少重复的代码,并且让路由路径的管理更加清晰。

下面是一个例子:

app.route('/book')
  .get((req, res) => {
    res.send('Get a random book')
  })
  .post((req, res) => {
    res.send('Add a book')
  })
  .put((req, res) => {
    res.send('Update the book')
  })

首先,创建了一个 /book 的路由路径,然后分别指定了处理 GET 请求、POST 请求和 PUT 请求的函数。这样,对于同一个路由路径,可以在一个地方集中管理不同请求的处理方式。

express.Router 类

使用 express.Router 类可以创建模块化的、可挂载的路由处理程序。一个 Router 实例是一个完整的中间件和路由系统;因此,它经常被称为“mini-app”。

下面是一个示例,创建了一个作为模块的路由,并在其中加载了一个中间件函数,定义了一些路由,并将路由模块挂载到主应用程序中的路径上。

首先,在 app 目录中创建一个名为 birds.js 的路由文件,代码如下:

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

// 此路由的中间件
const timeLog = (req, res, next) => {
  console.log('Time: ', Date.now())
  next()
}
router.use(timeLog)

// 定义主页路由
router.get('/', (req, res) => {
  res.send('Birds home page')
})
// 定义about页面路由
router.get('/about', (req, res) => {
  res.send('About birds')
})

// 导出路由模块,以便在应用中加载使用
module.exports = router

然后,在应用中加载路由模块:

// 导入路由模块
const birds = require('./birds')

// ...

// 使用路由模块,并指定挂载路径
app.use('/birds', birds)

这样,当用户访问 /birds/birds/about 时,路由模块中定义的路由将会被触发,并且 timeLog 中间件函数也会被调用并记录请求时间。

分享此文档

Express 路由

或复制链接

本页目录