摘要
-
比起回调函数,使用 Promise 来处理异步错误要显得优雅许多。
-
结合 Express 内置的错误处理机制和 Promise 极大地降低产生未捕获错误(uncaught exception)的可能性。
-
Promise 在ES6中是默认选项。如果使用 Babel 转译,它也可以与 Generators 或者 Async/Await 相结合。
本文主要阐述如何在 Express 中使用错误处理中间件(error-handling middleware)来高效处理异步错误。在 Github 上有对应代码实例可供参考。
首先,让我们一起了解 Express 提供的开箱即用的错误处理工具。然后,我们将探讨如何使用 Promise, Generators 以及 ES7 的 async/await 来简化错误处理流程。
Express 内置的异步错误处理
在默认情况下,Express 会捕获所有在路由处理函数中的抛出的异常,然后将它传给下一个错误处理中间件:
app.get('/', function (req, res) {
throw new Error('oh no!')
})
app.use(function (err, req, res, next) {
console.log(err.message) // 噢!不!
})复制代码
对于同步执行的代码,以上的处理已经足够简单。然而,当异步程序在执行时抛出异常的情况,Express 就无能为力。原因在于当你的程序开始执行回调函数时,它原来的栈信息已经丢失。
app.get('/', function (req, res) {
queryDb(function (er, data) {
if (er) throw er
})
})
app.use(function (err, req, res, next) {
// 这里拿不到错误信息
})复制代码
对于这种情况,可以使用 next 函数来将错误传递给下一个错误处理中间件
app.get('/', function (req, res, next) {
queryDb(function (err, data) {
if (err) return next(err)
// 处理数据
makeCsv(data, function (err, csv) {
if (err) return next(err)
// 处理 csv
})
})
})
app.use(function (err, req, res, next) {
// 处理错误
})复制代码
使用这种方法虽然一时爽,却带来了两个问题:
-
你需要显式地在错误处理中间件中分别处理不同的异常。
-
一些隐式异常并没有被处理(如尝试获取一个对象并不存在的属性)
利用 Promise 传递异步错误
在异步执行的程序中使用 Promise 处理任何显式或隐式的异常情况,只需要在 Promise 链尾加上.catch(next) 即可。
app.get('/', function (req, res, next) {
// do some sync stuff
queryDb()
.then(function (data) {
// 处理数据
return makeCsv(data)
})
.then(function (csv) {
// 处理 csv
})
.catch(next)
})
app.use(function (err, req, res, next) {
// 处理错误
})复制代码
现在,所有异步和同步程序都将被传递到错误处理中间件。棒棒的。
虽然 Promise 让异步错误的传递变得容易,但这样的代码仍然有一些冗长和刻板。这时候 promise generator 就派上了用场。
用 Generators 简化代码
如果你使用的环境原生支持 Generators,你可以手动实现以下的功能。不过这里我们将借用 Bluebird.coroutine 来说明如何使用 Promise generator 来简化刚才的代码。
尽管接下来的例子使用的是 bluebird ,其它 Promise 库(如 co)也都支持 Promise generator.
首先,我们需要使得 Express 路由函数与 Promise generator 兼容:
var Promise = require('bluebird')
function wrap (genFn) { // 1
var cr = Promise.coroutine(genFn) // 2
return function (req, res, next) { // 3
cr(req, res, next).catch(next) // 4
}
}复制代码
这个函数是一个高阶函数,它做了以下几件事情:(分别与代码片段中的注释对应)
-
以 Genrator 为唯一的输入
-
让这个函数懂得如何 yield promise
-
返回一个普通的 Express 路由函数
-
当这个函数被执行时,它会使用 coroutine 来 yield promise,捕获期间发生的异常,然后将其传递给 next 函数
借助这个函数,我们就可以这样构造路由函数:
app.get('/', wrap(function *(req, res) {
var data = yield queryDb()
// 处理数据
var csv = yield makeCsv(data)
// 处理 csv
}))
app.use(function (err, req, res, next) {
// 处理错误
})复制代码
现在,Express 的异步错误处理流程的可读性已经近乎令人满意,而且你可以像写同步执行的代码一样去书写异步执行的代码,唯一不要忘了的就是 yield promises。
然而这还不是终点,ES7 的 async/await 提议可以让代码变得更简洁。
使用 ES7 async/await
ES7 async/await 的行为就像 Promise Generator 一样,只不过它可以被用到更多的地方(如类方法或者胖箭头函数)。
为了在 Express 中使用 async/await,同时优雅地处理异步错误,我们仍然需要一个与上文提到的wrap 类似的函数:
let wrap = fn => (...args) => fn(...args).catch(args[2])复制代码
这样,我们就可以按底下这种方式书写路由函数:
app.get('/', wrap(async function (req, res) {
let data = await queryDb()
// 处理数据
let csv = await makeCsv(data)
// 处理 csv
}))复制代码
现在可以愉快地写代码了
有了对同步和异步错误的处理,你可以用新的方式来开发 Express App。但有两点需要注意:
- 要习惯使用 throw ,它使得你的代码目的明确,throw 会明确地将程序引到错误处理中间件,这对同步或异步的程序都是适用的。
- 遇到特殊情况,当你觉得有必要时,也可以自行 try/catch。
app.get('/', wrap(async (req, res) => {
if (!req.params.id) {
throw new BadRequestError('Missing Id')
}
let companyLogo
try {
companyLogo = await getBase64Logo(req.params.id)
} catch (err) {
console.error(err)
companyLogo = genericBase64Logo
}
}))复制代码
- 要习惯使用 custom error classes,如 BadRequestError,因为这可以让你在错误处理中间件中更方便地分类处理。
app.use(function (err, req, res, next) {
if (err instanceof BadRequestError) {
res.status(400)
return res.send(err.message)
}
...
})复制代码
需要注意
- 以上介绍的方法要求所有异步操作必须返回 promise。如果你的异步操作是使用回调函数的方式,你需要将其转化成 promise。(可以直接使用 Bluebird.promisifyAll 这类函数)
-
事件发射器(如 steams)仍然会导致未捕获异常,你需要注意合理地处理这类情况:
相关推荐
如果中间件抛出异常或调用 `next(new Error())`,错误处理中间件将被触发。 7. **自定义错误处理** 开发者可以通过创建自己的中间件来处理特定错误,这可以用来统一错误格式,提供友好的错误信息。 8. **中间件...
在Node.js Express框架中,捕获全局异常是确保应用程序健壮性和稳定性的重要环节。当某个路由或中间件抛出未捕获的异常时,如果不进行处理,可能会导致服务...根据项目的特性和需求,可以选择适合自己的异常处理策略。
在Express中,我们可以通过定义全局错误处理器来捕获未定义的路由,提供定制化的错误信息,同时结合异常处理中间件来应对服务器内部错误。这样,即使用户误输入了URL,也能确保他们收到有意义的反馈,而不是一片空白...
这种中间件通常用于捕获未处理的异常,并返回适当的错误消息给客户端。例如: ```javascript app.use(function (err, req, res, next) { console.error(err.stack); res.status(500).send('Something broke!'); }...
5. 错误处理:Express提供了一种优雅的方式来处理应用中的错误,通过使用专门的错误处理中间件,可以在全局范围内捕获并处理异常。 二、PM2部署实战 1. PM2简介:PM2是Node.js的进程管理工具,它可以保持应用在...
- 可以通过四参数形式的中间件(err, req, res, next)来处理异常。 6. **静态文件服务** - Express内置了静态文件服务功能,允许直接提供public目录下的资源。 - 使用app.use(express.static('public'))可以...
在JavaScript的Express框架中,处理异步函数的异常捕获是一项关键任务,因为不恰当的错误处理可能导致应用程序崩溃。在Express应用中,通常使用`async/await`语法来编写异步代码,但这样会使得每个异步函数内部都...
标题 "nodejs-demo-express4" ...通过这个项目,开发者不仅可以了解基本的Node.js和Express 4应用开发,还可以深入了解实际项目中如何组织代码、处理数据和实现特定功能,这对于提升Node.js Web开发技能非常有帮助。
9. 异常处理 在开发环境中,Express可以使用express.errorHandler()来帮助开发者处理异常,显示堆栈信息等。而在生产环境中,异常处理器应该更加精简,不包含敏感的错误信息。 通过这些知识点,可以看出Express框架...
在Node.js中,`domain`模块提供了一种机制,可以在一组相关的事件之间创建上下文,当这组事件中发生异常时,可以统一处理这些异常,而不是让程序崩溃。通过将I/O操作绑定到特定的域,可以更方便地捕获和管理异步错误...
5. **错误处理**:Express提供全局错误处理中间件,可以捕获应用中未被捕获的错误,确保优雅地处理异常。 三、安装与使用 在离线环境下,你可以下载Express的安装包并手动安装。首先,确保你已经安装了Node.js。...
"Express包用于自动将你的异常转成APIProblemJSON响应"这个标题暗示了一个特定的中间件或者插件,它能帮助开发者优雅地处理错误并将其转化为标准的API Problem JSON格式,这种格式在RESTful API设计中是广泛推荐的。...
此外,Express 还提供了错误处理机制,使得开发过程中能更好地捕获和处理异常。 总之,"nodejs_express_module" 旨在帮助开发者在遇到网络障碍时,仍能顺利部署和使用 Express 框架,从而实现快速构建 Node.js 后端...
4. **错误处理**:内置的错误处理中间件使得捕获和处理异常变得更加容易。 5. **连接器**:Express 可以轻松地与 MongoDB、MySQL、PostgreSQL 等数据库集成,提供数据持久化功能。 6. **可扩展性**:Express 的...
- Express内置了错误处理中间件,用于捕获和处理应用中的异常,提供了一种优雅的方式来处理错误。 - 在4.15.2中,错误处理可能更加健壮,能更好地处理各种可能出现的错误情况。 5. **中间件库**: - Express社区...
Express提供了全局错误处理中间件,用于捕获并统一处理可能出现的异常,确保系统的健壮性。 通过以上步骤,我们可以构建出一个功能完备的学生信息管理系统。在实际开发过程中,还可能涉及权限控制、分页、搜索过滤...
4. 错误处理:支持全局和局部错误处理器,方便异常捕获和处理。 **MyBatis** 是一个Java的持久层框架,专注于SQL映射和数据库操作。MyBatis的主要功能有: 1. 映射SQL:通过XML或注解方式将Java对象与SQL语句关联,...
9. **调试与错误处理**:学习如何使用Visual Web Developer的内置调试器来查找和修复代码错误,理解异常处理。 10. **部署Web应用**:了解将Web应用程序发布到Web服务器的过程,包括FTP发布和ClickOnce部署。 11. ...
在使用自定义session store时,务必处理可能的网络错误或存储异常,以免影响整个应用的稳定性。 总结,Express-Session是Express框架中的重要组成部分,它通过源码中的巧妙设计实现了高效且安全的会话管理。理解其...