Express.js 为上面定义的路由执行的中间件

Express.js middleware executing for a route defined above it

根据我的阅读 here and here,放置中间件功能的顺序很重要,因为如果将某些路由放在路由之前,您可以让某些路由不通过中间件功能,并且路由放在后面的会经过这个中间件函数。

我看到的结果好坏参半,因为我的开发环境不遵守这一点,而我的生产环境遵守。代码完全一样。

我想做的是让我的登录路由不受令牌检查器中间件功能的保护,并让我的其余路由受令牌保护。

这是我的代码:

routes.get('/login', function(req, res) {
    // login user, get token
});

routes.use(function(req, res, next) {
    // check header or url parameters or post parameters for token
    var token = req.headers['access-token'];
    // decode token
    if (token) {
        // validate token
    }
    else if (req.method === 'OPTIONS') {
        next();
    }
    else {
        // if there is no token
        // return an error
        return res.status(403).send({
            success: false,
            message: 'No token provided.'
        });
    }
});

routes.get('/query/:keywords', function(req, res) {
    console.log(req.params.keywords);
    // execute query
});

app.use('/', routes);

/query 路由是唯一应该通过令牌中间件功能的路由,对吗?现在我得到 /login 路由也通过令牌中间件功能,这没有意义,因为我不需要令牌才能登录。

更好的是,如果有一种方法可以确定我想要保护哪些路由以及我不想保护哪些路由,这似乎比必须依赖中间件函数放置位置的 "order" 要好.

由于路由顺序和登录路由从不调用下一个对象的事实,令牌中间件的应用不应该发生在登录路由上。如果没有更多信息,我们真的无法解决除此之外发生的事情,但是您可以 try inspecting it in your dev environment with a debugger 中断并查看命中该中间件的请求。

但是,我们可以为您提供一些有关如何尝试和隔离您的 .use 中间件以及如何应用中间件顺序的信息,以便您可以尝试将其与登录路由完全分开,就像在问题底部一样。


当仅将中间件应用于特定路由时,您应该注意 order 和 .use 用于中间件,它们应该在告诉 express 继续在路由器中寻找它们之后的其他中间件之前回答请求,这些中间件也将处理要求。如果你只想在几条路线上使用它,你可以像这样明确地将它添加到几条路线上:

router.get('/route', [ middleware1, middleware2, ..., middlewareX])

router.get('/route', middleware1, middleware2, ..., middlewareX)

两种模式都可以。然而,我发现数组模式更适合一些,因为我可以定义很多我想应用的中间件,然后为特定逻辑连接新的中间件,我只需要修改我声明连接的地方以添加更多功能。然而,很少需要那么多中间件,您应该可以使用其中任何一个。

您还可以通过使用路由器并将其作为第一个中间件应用到路由器之前的路由链,从而将该中间件划分为路由子集。

app.use('/user', authentication, userRouter)

或者您可以将它作为第一个带有 .use 的中间件放在路由器中,以便它处理所有请求。

所以请记住有关中间件使用的一般提示:

您可以在 expressjs documentation for middleware

中找到更多相关信息

首先,请遵循 ExpressJS 中的用法:

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

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

您会注意到它的定义与您在 routes.use(yourFunction(...)) 上声明的内容很接近。但是,除了遵循您在文档中看到的示例之外,没有真正的理由这样做,这是一个很好的开始方式。

但是,这是一个脆弱的实现,express 将允许在其 .get() .post() 方法中使用层次结构,这是正确的,但这是一个特定的用例,而不是您正在寻找的。

您需要的是使用双回调配置实现您的自定义身份验证过程。这样做:

// You can save this function in a separate file and import it with require() if you want

const tokenCheck = function(req, res, next) {
    // check header or url parameters or post parameters for token
    var token = req.headers['access-token'];
    // decode token
    if (token) {
        // validate token
    }
    else if (req.method === 'OPTIONS') {
        next();
    }
    else {
        // if there is no token
        // return an error
        return res.status(403).send({
            success: false,
            message: 'No token provided.'
        });
    }
});


routes.get('/login', function(req, res) {
    // login user, get token [Unprotected]
});

routes.get('/query/:keywords', tokenCheck, function(req, res) {
    console.log(req.params.keywords);
    // execute query [Protected with tokenCheck]
});

app.use('/', routes);

您可能需要尝试一下上面的代码,但它会引导您朝着正确的方向前进,这样您就可以指定特定的路由来根据需要执行 tokenCheck(req, res, next) 函数。

最简单的方法是使用路由器中间件来限定需要身份验证的路由和不需要身份验证的路由。由于所有路由器都是中间件,我们可以像实现任何其他中间件一样实现它们。确保我们按照我们希望评估路由的顺序放置路由器和路由。

在下面的示例中,Express 服务器有 2 个路由器,一个 LoginRouter 和一个 ApiRouter。

  • LoginRouter - 在接收到 POST /login 和 returns 的请求时生成令牌,请求者随后在 /api 路由中使用。
  • ApiRouter - 包装所有其他路由器,集中需要全局应用于 /api 下所有路由的中间件。只有经过身份验证的请求才能访问。

API 路由器只有在 Header 中包含一个令牌并且该令牌是从 LoginRouter 获得的情况下才能访问。 LoginRouter 不需要身份验证。

使用此设置,您将在 ApiRouter 上通过 .use() 将授权中间件之后的路由器添加到 API 路由器。

下面从其他 Router 组合 Router 的模式非常强大,可扩展且易于维护。

server.js

const express = require('express')
const bodyParser = require('bodyParser')
const ApiRouter = require('./routes/api')
const LoginRouter = require('./routes/login')
const port = process.env.PORT || 1337

const server = express()

server.use(bodyParser.json())

server.use('/login', LoginRouter)
server.use('/api', ApiRouter)

server.listen(port, () => console.log(`Listening on ${port}`))

登录路由器 - /routes/login.js

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

router.post('/', (req, res) => {    
    // Validate Credentials
    // some validation code...

    // Then create the token for use later in our API
    let token = '...'

    // Response 200 OK with the token in the message body   
    return res.status(200).send({token})
})

module.exports = router

ApiRouter - /routes/api/index.js

const router = require('express').Router()    
const UsersRouter = require('./routes/api/users')

router.use((req, res, next) => {
    let authorizationHeader = req.headers['authorization'] || req.headers['Authorization'] // handle lowercase
    let [, token] = authorizationHeader.split(' ')
    if (!token) {
        return res.sendStatus(403) // Forbidden, you're not logged in
    } else {
        // validate the token    
        if (!tokenIsValid) {
            return res.sendStatus(403) // Forbidden, invalid token
        } 

        // Everything is good, continue to the next middleware
        return next()
    }
})

router.use('/users', UsersRouter)

module.exports = router

用户路由器 - /routes/api/users

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

router.get('/', (req, res) => {
    // We only get here if the user is logged in     
    return res.status(200).json({users: []})
})

module.exports = router