路由

¥Routing

路由是指应用的端点 (URI) 如何响应客户端请求。有关路由的介绍,请参见 基本路由

¥Routing refers to how an application’s endpoints (URIs) respond to client requests. For an introduction to routing, see Basic routing.

你使用与 HTTP 方法相对应的 Express app 对象的方法来定义路由;例如,app.get() 处理 GET 请求,app.post 处理 POST 请求。如需完整列表,请参阅 app.METHOD。你还可以使用 app.all() 处理所有 HTTP 方法,使用 app.use() 指定中间件作为回调函数(详见 使用中间件)。

¥You define routing using methods of the Express app object that correspond to HTTP methods; for example, app.get() to handle GET requests and app.post to handle POST requests. For a full list, see app.METHOD. You can also use app.all() to handle all HTTP methods and app.use() to specify middleware as the callback function (See Using middleware for details).

这些路由方法指定了一个回调函数(有时称为 “处理函数”),当应用接收到对指定路由(端点)和 HTTP 方法的请求时调用。换句话说,应用 “监听” 对于匹配指定路由和方法的请求,当它检测到匹配时,它会调用指定的回调函数。

¥These routing methods specify a callback function (sometimes called “handler functions”) called when the application receives a request to the specified route (endpoint) and HTTP method. In other words, the application “listens” for requests that match the specified route(s) and method(s), and when it detects a match, it calls the specified callback function.

实际上,路由方法可以有多个回调函数作为参数。对于多个回调函数,重要的是提供 next 作为回调函数的参数,然后在函数体中调用 next() 以将控制权移交给下一个回调。

¥In fact, the routing methods can have more than one callback function as arguments. With multiple callback functions, it is important to provide next as an argument to the callback function and then call next() within the body of the function to hand off control to the next callback.

下面的代码是一个非常基本的路由示例。

¥The following code is an example of a very basic route.

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 类的实例。

¥A route method is derived from one of the HTTP methods, and is attached to an instance of the express class.

以下代码是为应用根目录的 GET 和 POST 方法定义的路由示例。

¥The following code is an example of routes that are defined for the GET and the POST methods to the root of the app.

// 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 请求方法的方法:getpost 等等。如需完整列表,请参阅 app.METHOD

¥Express supports methods that correspond to all HTTP request methods: get, post, and so on. For a full list, see app.METHOD.

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

¥There is a special routing method, app.all(), used to load middleware functions at a path for all HTTP request methods. For example, the following handler is executed for requests to the route “/secret” whether using GET, POST, PUT, DELETE, or any other HTTP request method supported in the http module.

app.all('/secret', (req, res, next) => {
  console.log('Accessing the secret section ...')
  next() // pass control to the next handler
})

路由路径

路由路径与请求方法相结合,定义了可以发出请求的端点。路由路径可以是字符串、字符串模式或正则表达式。

¥Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings, string patterns, or regular expressions.

字符 ?+*() 是它们的正则表达式对应物的子集。连字符 (-) 和点 (.) 由基于字符串的路径逐字解释。

¥The characters ?, +, *, and () are subsets of their regular expression counterparts. The hyphen (-) and the dot (.) are interpreted literally by string-based paths.

如果你需要在路径字符串中使用美元字符 ($),请将其包含在 ([]) 中进行转义。例如,位于 “/data/$book” 的请求的路径字符串将为“/data/([\$])book”。

¥If you need to use the dollar character ($) in a path string, enclose it escaped within ([ and ]). For example, the path string for requests at “/data/$book”, would be “/data/([\$])book”.

Express 使用 path-to-regexp 来匹配路由路径;有关定义路由路径的所有可能性,请参阅 path-to-regexp 文档。Express 路由测试器 是一个用于测试基本 Express 路由的便捷工具,尽管它不支持模式匹配。

¥Express uses path-to-regexp for matching the route paths; see the path-to-regexp documentation for all the possibilities in defining route paths. Express Route Tester is a handy tool for testing basic Express routes, although it does not support pattern matching.

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

¥Query strings are not part of the route path.

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

¥Here are some examples of route paths based on strings.

此路由路径将匹配对根路由 / 的请求。

¥This route path will match requests to the root route, /.

app.get('/', (req, res) => {
  res.send('root')
})

此路由路径将匹配对 /about 的请求。

¥This route path will match requests to /about.

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

此路由路径将匹配对 /random.text 的请求。

¥This route path will match requests to /random.text.

app.get('/random.text', (req, res) => {
  res.send('random.text')
})

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

¥Here are some examples of route paths based on string patterns.

此路由路径将匹配 acdabcd

¥This route path will match acd and abcd.

app.get('/ab?cd', (req, res) => {
  res.send('ab?cd')
})

此路由路径将匹配 abcdabbcdabbbcd 等。

¥This route path will match abcd, abbcd, abbbcd, and so on.

app.get('/ab+cd', (req, res) => {
  res.send('ab+cd')
})

此路由路径将匹配 abcdabxcdabRANDOMcdab123cd 等。

¥This route path will match abcd, abxcd, abRANDOMcd, ab123cd, and so on.

app.get('/ab*cd', (req, res) => {
  res.send('ab*cd')
})

此路由路径将匹配 /abe/abcde

¥This route path will match /abe and /abcde.

app.get('/ab(cd)?e', (req, res) => {
  res.send('ab(cd)?e')
})

基于正则表达式的路由路径示例:

¥Examples of route paths based on regular expressions:

此路由路径将匹配其中带有 “a” 的任何内容。

¥This route path will match anything with an “a” in it.

app.get(/a/, (req, res) => {
  res.send('/a/')
})

此路由路径将匹配 butterflydragonfly,但不匹配 butterflymandragonflyman 等。

¥This route path will match butterfly and dragonfly, but not butterflyman, dragonflyman, and so on.

app.get(/.*fly$/, (req, res) => {
  res.send('/.*fly$/')
})

路由参数

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

¥Route parameters are named URL segments that are used to capture the values specified at their position in the URL. The captured values are populated in the req.params object, with the name of the route parameter specified in the path as their respective keys.

Route path: /users/:userId/books/:bookId
Request URL: http://localhost:3000/users/34/books/8989
req.params: { "userId": "34", "bookId": "8989" }

要使用路由参数定义路由,只需在路由的路径中指定路由参数,如下所示。

¥To define routes with route parameters, simply specify the route parameters in the path of the route as shown below.

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

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

¥The name of route parameters must be made up of “word characters” ([A-Za-z0-9_]).

由于连字符 (-) 和点 (.) 是按字面解释的,因此它们可以与路由参数一起用于有用的目的。

¥Since the hyphen (-) and the dot (.) are interpreted literally, they can be used along with route parameters for useful purposes.

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" }

为了更好地控制路由参数可以匹配的确切字符串,你可以在括号 (()) 中附加正则表达式:

¥To have more control over the exact string that can be matched by a route parameter, you can append a regular expression in parentheses (()):

Route path: /user/:userId(\d+)
Request URL: http://localhost:3000/user/42
req.params: {"userId": "42"}

由于正则表达式通常是字面量字符串的一部分,因此请务必转义任何 \ characters with an additional backslash, for example \d+

¥Because the regular expression is usually part of a literal string, be sure to escape any \ characters with an additional backslash, for example \d+.

在 Express 4.x 中,正则表达式中的 * 字符不以通常的方式解释。作为解决方法,请使用 {0,} 而不是 *。这可能会在 Express 5 中得到修复。

¥In Express 4.x, the * character in regular expressions is not interpreted in the usual way. As a workaround, use {0,} instead of *. This will likely be fixed in Express 5.

路由处理程序

你可以提供多个回调函数,其行为类似于 中间件 来处理请求。唯一的例外是这些回调可能会调用 next('route') 来绕过剩余的路由回调。你可以使用此机制对路由施加先决条件,然后如果没有理由继续当前路由,则将控制权传递给后续路由。

¥You can provide multiple callback functions that behave like middleware to handle a request. The only exception is that these callbacks might invoke next('route') to bypass the remaining route callbacks. You can use this mechanism to impose pre-conditions on a route, then pass control to subsequent routes if there’s no reason to proceed with the current route.

路由处理程序的形式可以是函数、函数数组或两者的组合,如以下示例所示。

¥Route handlers can be in the form of a function, an array of functions, or combinations of both, as shown in the following examples.

单个回调函数可以处理路由。例如:

¥A single callback function can handle a route. For example:

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

一个以上的回调函数可以处理一个路由(确保你指定了 next 对象)。例如:

¥More than one callback function can handle a route (make sure you specify the next object). For example:

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

一组回调函数可以处理路由。例如:

¥An array of callback functions can handle a route. For example:

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])

独立函数和函数数组的组合可以处理路由。例如:

¥A combination of independent functions and arrays of functions can handle a route. For example:

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)上的方法可以向客户端发送响应,并终止请求-响应循环。如果没有从路由处理程序调用这些方法,则客户端请求将被挂起。

¥The methods on the response object (res) in the following table can send a response to the client, and terminate the request-response cycle. If none of these methods are called from a route handler, the client request will be left hanging.

方法 描述
res.download() 提示要下载的文件。
res.end() 结束响应过程。
res.json() 发送 JSON 响应。
res.jsonp() 发送带有 JSONP 支持的 JSON 响应。
res.redirect() 重定向请求。
res.render() 渲染视图模板。
res.send() 发送各种类型的响应。
res.sendFile() 将文件作为八位字节流发送。
res.sendStatus() 设置响应状态码并将其字符串表示形式作为响应正文发送。

app.route()

你可以使用 app.route() 为路由路径创建可链式的路由处理程序。因为路径是在单个位置指定的,所以创建模块化路由是有帮助的,因为这有助于减少冗余和拼写错误。有关路由的更多信息,请参阅:Router() 文档

¥You can create chainable route handlers for a route path by using app.route(). Because the path is specified at a single location, creating modular routes is helpful, as is reducing redundancy and typos. For more information about routes, see: Router() documentation.

这是使用 app.route() 定义的链式路由处理程序的示例。

¥Here is an example of chained route handlers that are defined by using 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

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

¥Use the express.Router class to create modular, mountable route handlers. A Router instance is a complete middleware and routing system; for this reason, it is often referred to as a “mini-app”.

以下示例将路由创建为模块,在其中加载中间件函数,定义一些路由,并将路由模块安装在主应用的路径上。

¥The following example creates a router as a module, loads a middleware function in it, defines some routes, and mounts the router module on a path in the main app.

在 app 目录下创建一个名为 birds.js 的路由文件,内容如下:

¥Create a router file named birds.js in the app directory, with the following content:

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

// middleware that is specific to this router
const timeLog = (req, res, next) => {
  console.log('Time: ', Date.now())
  next()
}
router.use(timeLog)

// 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

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

¥Then, load the router module in the app:

const birds = require('./birds')

// ...

app.use('/birds', birds)

该应用现在将能够处理对 /birds/birds/about 的请求,以及调用特定于路由的 timeLog 中间件函数。

¥The app will now be able to handle requests to /birds and /birds/about, as well as call the timeLog middleware function that is specific to the route.