Router
甲router
目的是中间件和路由的分离的实例。您可以将其视为“微型应用程序”,仅能够执行中间件和路由功能。每个Express应用程序都有一个内置的应用路由器。
路由器的行为类似于中间件本身,因此您可以将其用作 app.use()的参数或用作另一台路由器的use()方法的参数。
顶级express
对象具有创建新对象的Router()方法router
。
一旦你创建了一个路由器的对象,你可以添加中间件和HTTP方法路由(如get
,put
,post
,等),以它就像一个应用程序。例如:
// invoked for any requests passed to this router
router.use(function (req, res, next) {
// .. some logic here .. like any other middleware
next()
})
// will handle any request that ends in /events
// depends on where the router is "use()'d"
router.get('/events', function (req, res, next) {
// ..
})
然后,您可以将路由器用于特定的根URL,以这种方式将路由分为文件或什至是微型应用程序。
// only requests to /calendar/* will be sent to our "router"
app.use('/calendar', router)
方法
router.all(path, [callback, …] callback)
router.METHOD()
除了与所有HTTP方法(动词)匹配外,此方法与其他方法一样。
该方法对于映射“全局”逻辑以获得特定的路径前缀或任意匹配项非常有用。例如,如果将以下路由放在所有其他路由定义的顶部,则将要求从该点开始的所有路由都需要身份验证,并自动加载用户。请记住,这些回调不必充当端点。loadUser
可以执行任务,然后调用next()
以继续匹配后续路线。
router.all('*', requireAuthentication, loadUser)
或等效的:
router.all('*', requireAuthentication)
router.all('*', loadUser)
另一个例子是列入白名单的“全局”功能。这里的示例与之前非常相似,但是只限制了以“ / api”为前缀的路径:
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()
HTTPHEAD
方法调用该函数,则将自动为该方法调用该函数。GET``router.head()``router.get()
您可以提供多个回调,所有回调均被同等对待,并且其行为与中间件相同,不同之处在于,这些回调可能会调用next('route')
以绕过其余的路由回调。您可以使用此机制在路由上执行前提条件,然后在没有理由继续进行匹配的路由时将控制权传递给后续路由。
以下代码段说明了最简单的路由定义。Express将路径字符串转换为正则表达式,供内部使用以匹配传入的请求。执行这些匹配时不考虑查询字符串,例如“ GET /”将匹配以下路由,“ GET /?name = tobi”也将匹配。
router.get('/', function (req, res) {
res.send('hello world')
})
您也可以使用正则表达式-如果您有非常特定的约束,则很有用,例如,以下内容将匹配“ GET / commits / 71dbb9c”和“ GET /commits/71dbb9c..4c084f9”。
router.get(/^\/commits\/(\w+)(?:\.\.(\w+))?$/, function (req, res) {
var from = req.params[0]
var to = req.params[1] || 'HEAD'
res.send('commit range ' + from + '..' + to)
})
router.param(name, callback)
将回调触发器添加到路由参数,其中参数name
名称callback
是回调函数。尽管name
从技术上讲是可选的,但从Express v4.11.0开始,不推荐使用不带此方法的方法(请参见下文)。
回调函数的参数为:
req
,即请求对象。res
,即响应对象。next
,指示下一个中间件功能。name
参数的值。- 参数的名称。
与不同
app.param()
,router.param()
它不接受路由参数数组。
例如,当:user
路径中存在路径时,您可以映射用户加载逻辑以自动提供req.user
给路径,或对参数输入执行验证。
router.param('user', function (req, res, next, id) {
// try to get the user details from the User model and attach it to the request object
User.find(id, function (err, user) {
if (err) {
next(err)
} else if (user) {
req.user = user
next()
} else {
next(new Error('failed to load user'))
}
})
})
参数回调函数对于定义它们的路由器而言是本地的。它们不会被已安装的应用程序或路由器继承。因此,router
仅在路由上定义的路由参数将触发在上定义的参数回调router
。
即使参数在多个路由中匹配,在请求-响应周期中也仅会调用一次参数回调,如以下示例所示。
router.param('id', function (req, res, next, id) {
console.log('CALLED ONLY ONCE')
next()
})
router.get('/user/:id', function (req, res, next) {
console.log('although this matches')
next()
})
router.get('/user/:id', function (req, res) {
console.log('and this matches too')
res.end()
})
在上GET /user/42
,打印以下内容:
CALLED ONLY ONCE
although this matches
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
。为避免此现象,请为每个路由器使用不同的路径。