提问人:Daniel Kesner 提问时间:10/27/2023 最后编辑:Daniel Kesner 更新时间:11/15/2023 访问量:82
我应该如何从 Express 中间件中调用异步函数?
How should I call an async function from within Express middleware?
问:
我已经能够将这个问题简化为一个单文件示例,该示例应该可以在 Express 5.0.0-beta.1 中轻松重现。
我正在编写一个实现基本 JWT 身份验证的 Express 中间件函数。逻辑非常简单:从标头中提取令牌(如果不存在/格式不正确,则返回 401),然后将令牌插入我们将调用的内部函数中,该函数返回 Promise。注意:我相信这是 Stack Overflow 上的一个独特问题,这个问题是关于从端点处理程序中进行异步调用,但我需要从中间件函数中进行异步调用。Authorization
validateToken
我尝试通过以下方式实现这一点:
const express = require('express');
const app = express();
class HttpError {
constructor(status, message) {
this.status = status
this.message = message
}
}
// Auth middleware
const authMiddleware = (req, res, next) => {
const token = req.header('Authorization');
if (!token) {
console.info("No JWT in request")
return next(new HttpError(401, 'Unauthorized'))
}
const regex = /^Bearer (.+)$/;
const jwt = regex.exec(token)[1];
if (!jwt) {
console.info("Malformed JWT")
return next(new HttpError(401, 'Unauthorized'))
}
return validateToken({jwt: jwt, path: '/secured'})
.then((response) => next())
.catch(err => next(new HttpError(403, 'Forbidden')))
}
app.use(authMiddleware)
// Endpoint definitions
app.get('/secured', (req, res) => {
console.info('endpoint handler invoked')
res.send('Valid token')
})
// Error handlers
function errorMiddleware(error, req, res, next) {
console.info(`error middleware hit: ${JSON.stringify(error)}`)
if (!error.status || error.status >= 500) {
return res.status(error.status || 500).send();
}
res.status(error.status).json({ message: error.message });
}
app.use(errorMiddleware)
app.listen(8080);
当我发送一个没有标头的请求并且触发了前两个块之一(返回)时,应用程序会立即向客户端发出 401 Unauthorized 响应。但是,当我发送标头中带有令牌的请求时,应用程序会无限期地挂在中间件函数的最后一行。Authorization
if
next(new HttpError(...)
return validateToken(...)
有人告诉我,Express 5.x 会自动处理中间件函数中被拒绝的承诺,但即使在升级到 Express 5 之后,应用程序在调用异步函数时仍然无限期挂起。validateToken
答:
事实证明,我调用的函数有一些非常奇怪的行为 - 在验证令牌之前,它等待对 AWS 元数据服务的 HTTP 调用以获取有关系统的一些信息。我以为我的异步 Express 代码犯了一个错误,但实际上对 AWS 的调用需要 75 秒才能超时,超时后,处理程序的功能完全符合预期。validateToken
下面是一个 Express 服务器的单文件示例,它等待中间件中的异步操作,希望这对将来的人有所帮助:
const express = require('express');
const app = express();
class HttpError {
constructor(status, message) {
this.status = status
this.message = message
}
}
// Auth middleware
const authMiddleware = (req, res, next) => {
const token = req.header('Authorization');
if (!token) {
console.info("No JWT in request")
return next(new HttpError(401, 'Unauthorized'))
}
const regex = /^Bearer (.+)$/;
const jwt = regex.exec(token)[1];
if (!jwt) {
console.info("Malformed JWT")
return next(new HttpError(401, 'Unauthorized'))
}
// Assume that `validateToken` is a function returning a promise with either the token's claims or an error
return this.validateToken({jwt: jwt, path: '/secured'})
.then((response) => next())
.catch(err => next(new HttpError(403, 'Forbidden')))
}
// Invoke authMiddleware function for all endpoints that begin with '/secured'
app.use('/secured', authMiddleware)
// Endpoint definitions
app.get('/secured', (req, res) => {
res.send('Valid token')
})
app.get('/unsecured', (req, res) => res.send('Unsecured endpoint'))
// Error handlers
function errorMiddleware(error, req, res, next) {
console.debug(`Error caught by Express error handler: ${JSON.stringify(error)}`)
if (!error.status || error.status >= 500) {
return res.status(error.status || 500).send();
}
res.status(error.status).json({ message: error.message });
}
app.use(errorMiddleware)
app.listen(8080);
评论
next(error)
catch
validateToken
async