Router

router目的是中间件和路由的分离的实例。您可以将其视为“微型应用程序”,仅能够执行中间件和路由功能。每个Express应用程序都有一个内置的应用路由器。
路由器的行为类似于中间件本身,因此您可以将其用作 app.use()的参数或用作另一台路由器的use()方法的参数。
顶级express对象具有创建新对象的Router()方法router
一旦你创建了一个路由器的对象,你可以添加中间件和HTTP方法路由(如getputpost,等),以它就像一个应用程序。例如:

  1. // invoked for any requests passed to this router
  2. router.use(function (req, res, next) {
  3. // .. some logic here .. like any other middleware
  4. next()
  5. })
  6. // will handle any request that ends in /events
  7. // depends on where the router is "use()'d"
  8. router.get('/events', function (req, res, next) {
  9. // ..
  10. })

然后,您可以将路由器用于特定的根URL,以这种方式将路由分为文件或什至是微型应用程序。

  1. // only requests to /calendar/* will be sent to our "router"
  2. app.use('/calendar', router)

方法

router.all(path, [callback, …] callback)

router.METHOD()除了与所有HTTP方法(动词)匹配外,此方法与其他方法一样。
该方法对于映射“全局”逻辑以获得特定的路径前缀或任意匹配项非常有用。例如,如果将以下路由放在所有其他路由定义的顶部,则将要求从该点开始的所有路由都需要身份验证,并自动加载用户。请记住,这些回调不必充当端点。loadUser 可以执行任务,然后调用next()以继续匹配后续路线。

  1. router.all('*', requireAuthentication, loadUser)

或等效的:

  1. router.all('*', requireAuthentication)
  2. router.all('*', loadUser)

另一个例子是列入白名单的“全局”功能。这里的示例与之前非常相似,但是只限制了以“ / api”为前缀的路径:

  1. router.all('/api/*', requireAuthentication)

router.METHOD(path, [callback, …] callback)

这些router.METHOD()方法在Express中提供了路由功能,其中METHOD是小写的HTTP方法之一,例如GET,PUT,POST等。因此,实际的方法是router.get()router.post()router.put(),等等。

如果以前没有为router.get()HTTP HEAD方法调用该函数,则将自动为该方法调用该函数。GET``router.head()``router.get()

您可以提供多个回调,所有回调均被同等对待,并且其行为与中间件相同,不同之处在于,这些回调可能会调用next('route') 以绕过其余的路由回调。您可以使用此机制在路由上执行前提条件,然后在没有理由继续进行匹配的路由时将控制权传递给后续路由。
以下代码段说明了最简单的路由定义。Express将路径字符串转换为正则表达式,供内部使用以匹配传入的请求。执行这些匹配时考虑查询字符串,例如“ GET /”将匹配以下路由,“ GET /?name = tobi”也将匹配。

  1. router.get('/', function (req, res) {
  2. res.send('hello world')
  3. })

您也可以使用正则表达式-如果您有非常特定的约束,则很有用,例如,以下内容将匹配“ GET / commits / 71dbb9c”和“ GET /commits/71dbb9c..4c084f9”。

  1. router.get(/^\/commits\/(\w+)(?:\.\.(\w+))?$/, function (req, res) {
  2. var from = req.params[0]
  3. var to = req.params[1] || 'HEAD'
  4. res.send('commit range ' + from + '..' + to)
  5. })

router.param(name, callback)

将回调触发器添加到路由参数,其中参数name名称callback是回调函数。尽管name从技术上讲是可选的,但从Express v4.11.0开始,不推荐使用不带此方法的方法(请参见下文)。
回调函数的参数为:

  • req,即请求对象。
  • res,即响应对象。
  • next,指示下一个中间件功能。
  • name参数的值。
  • 参数的名称。

    与不同app.param()router.param()它不接受路由参数数组。

例如,当:user路径中存在路径时,您可以映射用户加载逻辑以自动提供req.user给路径,或对参数输入执行验证。

  1. router.param('user', function (req, res, next, id) {
  2. // try to get the user details from the User model and attach it to the request object
  3. User.find(id, function (err, user) {
  4. if (err) {
  5. next(err)
  6. } else if (user) {
  7. req.user = user
  8. next()
  9. } else {
  10. next(new Error('failed to load user'))
  11. }
  12. })
  13. })

参数回调函数对于定义它们的路由器而言是本地的。它们不会被已安装的应用程序或路由器继承。因此,router仅在路由上定义的路由参数将触发在上定义的参数回调router
即使参数在多个路由中匹配,在请求-响应周期中也仅会调用一次参数回调,如以下示例所示。

  1. router.param('id', function (req, res, next, id) {
  2. console.log('CALLED ONLY ONCE')
  3. next()
  4. })
  5. router.get('/user/:id', function (req, res, next) {
  6. console.log('although this matches')
  7. next()
  8. })
  9. router.get('/user/:id', function (req, res) {
  10. console.log('and this matches too')
  11. res.end()
  12. })

在上GET /user/42,打印以下内容:

  1. CALLED ONLY ONCE
  2. although this matches
  3. and this matches too

以下部分介绍router.param(callback),从v4.11.0开始不推荐使用。

router.param(name, callback)通过仅将函数传递给,可以完全改变方法的行为router.param()。此函数是router.param(name, callback)行为方式的自定义实现-接受两个参数,并且必须返回中间件。
该函数的第一个参数是应捕获的URL参数的名称,第二个参数可以是可用于返回中间件实现的任何JavaScript对象。
函数返回的中间件决定了捕获URL参数时所发生的行为。
在此示例中,router.param(name, callback)签名被修改为router.param(name, accessId)router.param()现在将接受名称和数字,而不是接受名称和回调。

var express = require('express')
var app = express()
var router = express.Router()
// customizing the behavior of router.param()
router.param(function (param, option) {
  return function (req, res, next, val) {
    if (val === option) {
      next()
    } else {
      res.sendStatus(403)
    }
  }
})
// using the customized router.param()
router.param('id', '1337')
// route to trigger the capture
router.get('/user/:id', function (req, res) {
  res.send('OK')
})
app.use(router)
app.listen(3000, function () {
  console.log('Ready')
})

在此示例中,router.param(name, callback)签名保持不变,但是已定义了自定义数据类型检查功能来验证用户ID的数据类型,而不是中间件回调。

router.param(function (param, validator) {
  return function (req, res, next, val) {
    if (validator(val)) {
      next()
    } else {
      res.sendStatus(403)
    }
  }
})
router.param('id', function (candidate) {
  return !isNaN(parseFloat(candidate)) && isFinite(candidate)
})

router.route(path)

返回单个路由的实例,然后您可以使用该路由通过可选的中间件来处理HTTP动词。使用router.route()以避免重复路线的命名,因此打字错误。
router.param()上面的示例的基础上,以下代码显示了如何 router.route()用于指定各种HTTP方法处理程序。

var router = express.Router()
router.param('user_id', function (req, res, next, id) {
  // sample user, would actually fetch from DB, etc...
  req.user = {
    id: id,
    name: 'TJ'
  }
  next()
})
router.route('/users/:user_id')
  .all(function (req, res, next) {
    // runs for all HTTP verbs first
    // think of it as route specific middleware!
    next()
  })
  .get(function (req, res, next) {
    res.json(req.user)
  })
  .put(function (req, res, next) {
    // just an example of maybe updating the user
    req.user.name = req.params.name
    // save user ... etc
    res.json(req.user)
  })
  .post(function (req, res, next) {
    next(new Error('not implemented'))
  })
  .delete(function (req, res, next) {
    next(new Error('not implemented'))
  })

这种方法重用了单个/users/:user_id路径,并为各种HTTP方法添加了处理程序。

注意:使用时router.route(),中间件排序基于创建路由的时间,而不是基于将方法处理程序添加到路由的时间。为此,您可以考虑方法处理程序属于它们添加到的路由。

router.use([path], [function, …] function)

使用指定的一个或多个中间件功能以及可选的安装路径path,默认为“ /”。
此方法类似于app.use()。下面描述了一个简单的示例和用例。有关更多信息,请参见app.use()
中间件就像管道:请求从定义的第一个中间件功能开始,并按照它们匹配的每个路径“向下”进行中间件堆栈处理。

var express = require('express')
var app = express()
var router = express.Router()
// simple logger for this router's requests
// all requests to this router will first hit this middleware
router.use(function (req, res, next) {
  console.log('%s %s %s', req.method, req.url, req.path)
  next()
})
// this will only be invoked if the path starts with /bar from the mount point
router.use('/bar', function (req, res, next) {
  // ... maybe some additional /bar logging ...
  next()
})
// always invoked
router.use(function (req, res, next) {
  res.send('Hello World')
})
app.use('/foo', router)
app.listen(3000)

“安装”路径被剥离,并且对中间件功能可见。此功能的主要作用是,无论其“前缀”路径名如何,安装的中间件功能都可以在不更改代码的情况下运行。
定义中间件所用的顺序router.use()非常重要。它们被顺序调用,因此顺序定义了中间件优先级。例如,通常记录器是您要使用的第一个中间件,因此每个请求都会被记录。

var logger = require('morgan')
var path = require('path')
router.use(logger())
router.use(express.static(path.join(__dirname, 'public')))
router.use(function (req, res) {
  res.send('Hello')
})

现在,假设您想忽略对静态文件的日志记录请求,但是继续记录在之后定义的路由和中间件logger()express.static()在添加记录器中间件之前,您只需将调用移至顶部即可:

router.use(express.static(path.join(__dirname, 'public')))
router.use(logger())
router.use(function (req, res) {
  res.send('Hello')
})

另一个示例是提供多个目录中的文件,将“ ./public”优先于其他目录:

router.use(express.static(path.join(__dirname, 'public')))
router.use(express.static(path.join(__dirname, 'files')))
router.use(express.static(path.join(__dirname, 'uploads')))

router.use()方法还支持命名参数,以便其他路由器的安装点可以受益于使用命名参数的预加载。
注意:尽管这些中间件功能是通过特定的路由器添加的,但是 它们的运行时间是由它们附加的路径(而不是路由器)定义的。因此,如果一个路由器添加的中间件的路由匹配,则该中间件可以为其他路由器运行。例如,此代码显示了安装在同一路径上的两个不同的路由器:

var authRouter = express.Router()
var openRouter = express.Router()
authRouter.use(require('./authenticate').basic(usersdb))
authRouter.get('/:user_id/edit', function (req, res, next) {
  // ... Edit user UI ...
})
openRouter.get('/', function (req, res, next) {
  // ... List users ...
})
openRouter.get('/:user_id', function (req, res, next) {
  // ... View user ...
})
app.use('/users', authRouter)
app.use('/users', openRouter)

即使通过身份验证中间件添加了身份验证中间件,由于两个路由器都安装在上,authRouter因此它将在由定义的路由openRouter上运行/users。为避免此现象,请为每个路由器使用不同的路径。