专栏名称: Fundebug
Fundebug为JavaScript、微信小程序及Node.js开发团队提供专业的线上代码bug监控和智能分析服务。
目录
相关文章推荐
前端早读课  ·  【第3416期】JavaScript的??= ... ·  昨天  
Seebug漏洞平台  ·  原创 Paper | CodeQL 入门和基本使用 ·  3 天前  
Seebug漏洞平台  ·  原创 Paper | CodeQL 入门和基本使用 ·  3 天前  
前端大全  ·  CSS 函数 calc() 会引起重排重绘吗 ·  1 周前  
光伏资讯  ·  BC阵营大合体! ·  1 周前  
光伏资讯  ·  BC阵营大合体! ·  1 周前  
麦家陪你读书  ·  麦家:童年受过的伤害,身体会一直记得 ·  1 周前  
51好读  ›  专栏  ›  Fundebug

如何处理Express异常?

Fundebug  · 公众号  · 前端  · 2017-12-06 08:59

正文


译者按:根据墨菲定律:“有可能出错的事情,就会出错”。那么,既然代码必然会出错,我们就应该处理好异常。

  • 原文: How to handle errors in Express

  • 译者:Fundebug

为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。

处理异常是编程非常重要的一点。我们的程序依赖于第三方服务、数据库以及我们的用户,一切都不可预料。数据库可能会宕机,第三方服务可能会崩溃,用户可能会使用错误的参数调用我们的接口。

为了处理各种复杂的情况,我们必须处理好代码异常,下面是代码示例:

app.get('/users/:id', (req, res) => {

const userId = req.params.id

if (!userId) {

return res.sendStatus(400).json({

error: 'Missing id'

})

}

Users.get(userId, (err, user) => {

if (err) {

return res.sendStatus(500).json(err)

}

res.send(users)

})

})

代码中处理了异常,但是存在问题:

  • 在多处代码处理异常

  • 没有使用Express的异常处理模块来统一处理异常

接下来,我们来一步步优化代码异常处理。

Express异常处理中间件

所有Express的路由处理函数都有第三个参数next,它可以用来调用下一个中间件,也可以将错误传递给错误处理中间件:

app.get('/users/:id', (req, res, next) => {

const userId = req.params.id

if (!userId) {

const error = new Error('missing id')

error.httpStatusCode = 400

return next(error)

}

Users.get(userId, (err, user) => {

if (err) {

err.httpStatusCode = 500

return next(err)

}

res.send(users)

})

})

使用next(err),Express就知道出错了,并把这个错误传递给错误处理模块。为了处理这些错误,需要添加一个中间件,它有4个参数:

app.use((err, req, res, next) => {

// log the error...

res.sendStatus(err.httpStatusCode).json(err)

})

这样,我们就可以使用中间件统一处理错误了。但是,现在的代码有些重复:创建错误,指定HTTP状态码,使用next(err)…

Fundebug是全栈JavaScript错误监控平台,支持各种前端和后端框架,可以帮助您第一时间发现BUG!

boom

boom是一个兼容HTTP的错误对象,他提供了一些标准的HTTP错误,比如400(参数错误)等。

const boom = require('boom')

app.get('/users/:id', (req, res, next) => {

const userId = req.params.id

if (!userId) {

return next(boom.badRequest('missing id'))

}

Users.get(userId, (err, user) => {

if (err) {

return next(boom.badImplementation(err))

}

res.send(users)

})

})

错误处理中间件需要稍作修改:

app.use((err, req, res, next) => {

if (err.isServer) {

// log the error...

// probably you don't want to log unauthorized access

// or do you?

}

return res.status(err.output.statusCode).json(err.output.payload);

})

Async/Await错误处理

使用Async/Await之后,可以这样处理Express异常:

  • 将中间件使用Promise封装起来,使用catch统一处理异常

  • 在中间件中,直接抛出异常就可以了

const boom = require('boom');

// wrapper for our async route handlers

// probably you want to move it to a new file

const asyncMiddleware = fn => (req, res, next) => {

Promise.resolve(fn(req, res, next)).catch((err) => {

if (!err.isBoom) {

return next(boom.badImplementation(err));

}

next(err);

});

};

// the async route handler

app.get('/users/:id', asyncMiddleware(async (req, res) => {

const userId = req.params.id

if (!userId) {

throw boom.badRequest('missing id')

}

const user = await Users.get(userId)

res.json(user)

}))

参考

  • 验证HTTP请求参数可以使用joi模块

  • 打印日志可以使用winston或者pino模块

我们欢迎转载,但是要求注明作者Fundebug保留原文地址并且附上Fundebug公众号的二维码。否则,我们将委托相关机构追诉法律责任。