[英]express.js async router and error handling
我有一个异步函数作为路由处理程序,我希望将错误作为某种中间件处理。 这是我的工作尝试:
router.get(
"/",
asyncMiddleware(
routeProviderMiddleware(
async ({ y }) => ({
body: await db.query({x: y})
})
)
)
)
// This is the middleware that catches any errors from the business logic and calls next to render the error page
const asyncMiddleware = fn =>
(req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch(next)
}
// This is a middleware that provides the route handler with the query and maybe some other services that I don't want the route handler to explicitly access to
const routeProviderMiddleware = routeHandlerFn => async (req, res) => {
const {status = 200, body = {}} = await routeHandlerFn(req.query)
res.status(status).json(body)
}
我努力的是一种使路由声明更清晰的方法——我不想要那里的 2 个中间件包装器,理想情况下我只想要那里的业务逻辑功能,并以某种方式声明每条路由都包含在其中。 即使将这两个中间件组合在一起也很好,但我没有做到。
我使用以下方法:
创建asyncWrap
作为辅助中间件:
const asyncWrap = fn =>
function asyncUtilWrap (req, res, next, ...args) {
const fnReturn = fn(req, res, next, ...args)
return Promise.resolve(fnReturn).catch(next)
}
module.exports = asyncWrap
你所有的路由/中间件/控制器都应该使用这个asyncWrap
来处理错误:
router.get('/', asyncWrap(async (req, res, next) => {
let result = await db.query({x: y})
res.send(result)
}));
在app.js
中,最后一个中间件会收到所有asyncWrap
的错误:
// 500 Internal Errors
app.use((err, req, res, next) => {
res.status(err.status || 500)
res.send({
message: err.message,
errors: err.errors,
})
})
Express 5 自动正确处理异步错误
https://expressjs.com/en/guide/error-handling.html目前说的很清楚:
从 Express 5 开始,返回 Promise 的路由处理程序和中间件将在拒绝或抛出错误时自动调用 next(value) 。 例如:
app.get('/user/:id', async function (req, res, next) { var user = await getUserById(req.params.id) res.send(user) })
如果 getUserById 抛出错误或拒绝,则将使用抛出的错误或拒绝的值调用 next。 如果没有提供拒绝值,next 将使用 Express 路由器提供的默认错误对象调用。
我已经在一个实验中证明了这一点: 将异步函数传递给 Node.js Express.js 路由器
这意味着您将能够使回调async
并直接从中使用await
,而无需任何额外的包装器:
router.get("/", async (req, res) =>
const obj = await db.query({x: req.params.id})
// Use obj normally.
)
并且错误将被自动正确处理。
Express 允许一个路由的中间件列表,这种方法有时比高阶函数更适合我(它们有时看起来像是过度设计)。
例子:
app.get('/',
validate,
process,
serveJson)
function validate(req, res, next) {
const query = req.query;
if (isEmpty(query)) {
return res.status(400).end();
}
res.locals.y = query;
next();
}
function process(req, res, next) {
Promise.resolve()
.then(async () => {
res.locals.data = await db.query({x: res.locals.y});
next();
})
.catch((err) =>
res.status(503).end()
);
}
function serveJson(req, res, next) {
res.status(200).json(res.locals.data);
}
你可以做的是在你的路线之后添加一个错误处理程序。 https://expressjs.com/en/guide/error-handling.html
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
我最终做的是像这样统一包装器:
const routeProvider = routeHandlerFn => async (req, res, next) => {
try {
const {status = 200, body = {}} = await routeHandlerFn(req.query)
res.status(status).json(body)
} catch(error) {
next(error)
}
}
这个包装器是任何路由都需要的。 它捕获意外错误并为路由处理程序提供所需的参数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.