路由是指应用程序的端点 (URI) 如何响应客户端请求。 有关路由的介绍,请参见 基本路由。
您使用与 HTTP 方法相对应的 Express app
对象的方法来定义路由;
例如,app.get()
处理 GET 请求,app.post
处理 POST 请求。 如需完整列表,请参阅 app.METHOD。 您还可以使用 app.all() 处理所有 HTTP 方法,使用 app.use() 指定中间件作为回调函数(详见 使用中间件)。
这些路由方法指定了一个回调函数(有时称为 “处理函数”),当应用程序接收到对指定路由(端点)和 HTTP 方法的请求时调用。 换句话说,应用程序 “listens” 对于匹配指定路由和方法的请求,当它检测到匹配时,它会调用指定的回调函数。
实际上,路由方法可以有多个回调函数作为参数。
对于多个回调函数,重要的是提供 next
作为回调函数的参数,然后在函数体中调用 next()
以将控制权移交给下一个回调。
下面的代码是一个非常基本的路由示例。
const express = require('express')
const app = express()
// respond with "hello world" when a GET request is made to the homepage
app.get('/', (req, res) => {
res.send('hello world')
})
路由方法派生自 HTTP 方法之一,并附加到 express
类的实例。
以下代码是为应用程序根目录的 GET 和 POST 方法定义的路由示例。
// GET method route
app.get('/', (req, res) => {
res.send('GET request to the homepage')
})
// POST method route
app.post('/', (req, res) => {
res.send('POST request to the homepage')
})
Express 支持对应所有 HTTP 请求方法的方法: get
、post
等等。
如需完整列表,请参阅 app.METHOD。
有一种特殊的路由方法,app.all()
,用于在所有 HTTP 请求方法的路径上加载中间件函数。 例如,无论使用 GET、POST、PUT、DELETE 还是 http 模块 中支持的任何其他 HTTP 请求方法,都会对路由 “/secret” 的请求执行以下处理程序。
app.all('/secret', (req, res, next) => {
console.log('Accessing the secret section ...')
next() // pass control to the next handler
})
路由路径与请求方法相结合,定义了可以发出请求的端点。 路由路径可以是字符串、字符串模式或正则表达式。
字符 ?
、+
、*
和 ()
是它们的正则表达式对应物的子集。 连字符 (-
) 和点 (.
) 由基于字符串的路径逐字解释。
如果您需要在路径字符串中使用美元字符 ($
),请将其包含在 ([
和 ])
中进行转义。 例如,”/data/$book
” 处请求的路径字符串将是 “/data/([\$])book
“。
Express 使用 path-to-regexp 来匹配路由路径; 有关定义路由路径的所有可能性,请参阅 path-to-regexp 文档。 Express 路由测试器 是一个用于测试基本 Express 路由的便捷工具,尽管它不支持模式匹配。
查询字符串不是路由路径的一部分。
以下是一些基于字符串的路由路径示例。
此路由路径将匹配对根路由 /
的请求。
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$/')
})
路由参数是命名的 URL 段,用于捕获在 URL 中的位置指定的值。 捕获的值填充到 req.params
对象中,路径中指定的路由参数的名称作为它们各自的键。
Route path: /users/:userId/books/:bookId
Request URL: http://localhost:3000/users/34/books/8989
req.params: { "userId": "34", "bookId": "8989" }
要使用路由参数定义路由,只需在路由的路径中指定路由参数,如下所示。
app.get('/users/:userId/books/:bookId', (req, res) => {
res.send(req.params)
})
路由参数的名称必须由 “word characters” ([A-Za-z0-9_]) 组成。
由于连字符 (-
) 和点 (.
) 是按字面解释的,因此它们可以与路由参数一起用于有用的目的。
Route path: /flights/:from-:to
Request URL: http://localhost:3000/flights/LAX-SFO
req.params: { "from": "LAX", "to": "SFO" }
Route path: /plantae/:genus.:species
Request URL: http://localhost:3000/plantae/Prunus.persica
req.params: { "genus": "Prunus", "species": "persica" }
为了更好地控制路由参数可以匹配的确切字符串,您可以在括号 (()
) 中附加正则表达式:
Route path: /user/:userId(\d+)
Request URL: http://localhost:3000/user/42
req.params: {"userId": "42"}
因为正则表达式通常是文字字符串的一部分,所以一定要使用额外的反斜杠来转义任何 \
字符,例如 \\d+
。
在 Express 4.x 中,the *
character in regular expressions is not interpreted in the usual way。 作为一种解决方法,请使用 {0,}
而不是 *
。 这可能会在 Express 5 中修复。
您可以提供多个回调函数,其行为类似于 中间件 来处理请求。 唯一的例外是这些回调可能会调用 next('route')
来绕过剩余的路由回调。 您可以使用此机制对路由施加先决条件,然后如果没有理由继续当前路由,则将控制权传递给后续路由。
路由处理程序的形式可以是函数、函数数组或两者的组合,如以下示例所示。
单个回调函数可以处理路由。 例如:
app.get('/example/a', (req, res) => {
res.send('Hello from A!')
})
一个以上的回调函数可以处理一个路由(确保你指定了 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() | 重定向请求。 |
res.render() | 渲染视图模板。 |
res.send() | 发送各种类型的响应。 |
res.sendFile() | 将文件作为八位字节流发送。 |
res.sendStatus() | 设置响应状态码并将其字符串表示形式作为响应正文发送。 |
您可以使用 app.route()
为路由路径创建可链接的路由处理程序。
因为路径是在单个位置指定的,所以创建模块化路由是有帮助的,因为这有助于减少冗余和拼写错误。 有关路由的更多信息,请参阅: Router() 文档。
这是使用 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')
})
使用 express.Router
类创建模块化、可安装的路由处理程序。 一个Router
实例就是一个完整的中间件和路由系统; 因此,它通常被称为 “mini-app”。
以下示例将路由创建为模块,在其中加载中间件函数,定义一些路由,并将路由模块安装在主应用程序的路径上。
在app目录下创建一个名为birds.js
的路由文件,内容如下:
const express = require('express')
const router = express.Router()
// middleware that is specific to this router
router.use((req, res, next) => {
console.log('Time: ', Date.now())
next()
})
// define the home page route
router.get('/', (req, res) => {
res.send('Birds home page')
})
// define the about route
router.get('/about', (req, res) => {
res.send('About birds')
})
module.exports = router
然后,在应用程序中加载路由模块:
const birds = require('./birds')
// ...
app.use('/birds', birds)
该应用程序现在将能够处理对 /birds
和 /birds/about
的请求,以及调用特定于路由的 timeLog
中间件函数。