koa错误处理
使用koa的时候,对错误的处理是比较方便直接的,我们可以写一个以下的中间件来处理错误:
module.exports = function () {
return async (ctx, next) => {
try {
await next()
// 通过ctx来判断程序执行状态,进行一些额外的处理
} catch (err) {
// 处理throw错误
}
}
}
这个中间件需要在路由之前引入。这样next就是去处理路由的业务逻辑,并且由于try catch的存在, throw出来的error都可以被捕获到并返回给接口调用方。这样可以有一个强一致的接口返回数据结构,避免造成前端代码错误。
同时,可以在接口里自定义错误格式,比如接口参数错误,登录密码错误等,在接口代码里throw出来,然后在这个中间件里进行统一识别处理,这样,可以做到错误的统一处理,代码可以写的比较简洁。
express错误处理
用过express的人都知道,express的错误是通过next(err)来传导的,这样,throw的错误假如没有通过代码去捕获的话,很容易造成整个express app都崩溃。很多人写代码都是在每个中间件里进行try catch, 然后把catch到的err通过next(err)通知express进行错误处理。这样会有很多重复的代码,而且无法做到接口代码无感知处理接口错误。
我们可以写一个适配器处理这个问题:
module.exports = class AppRouteAdapter {
constructor (realApp) {
// realApp是express实例
this.realApp = realApp
// 这里支持get, put,post,patch, delete方法,假如需要支持新的方法,可以加在这个数组里面
let methods = ['get', 'put', 'post', 'patch', 'delete']
methods.forEach((action) => {
// 动态添加http请求方法
this[action] = this.method.bind(this, action)
})
}
_isAsyncFunc (func) {
// console.log(this.curApi, func)
const string = func.toString().trim()
return string.indexOf('async') === 0
}
_toAsyncMiddleware (middleware) {
let realApp = this.realApp
return this._isAsyncFunc(middleware) ? middleware : async function () { middleware.apply(realApp, Array.prototype.slice.call(arguments, 0)) }
}
// 把路由中间件合并成一个,支持中间件嵌套执行。
_combineMiddleware (middlewares = []) {
let realApp = this.realApp
return async function (req, res, next) {
let nextMiddleware = next
for (let i = middlewares.length - 1; i >= 0; i--) {
nextMiddleware = middlewares[i].bind(realApp, req, res, nextMiddleware)
}
return nextMiddleware()
}
}
route (api) {
this.curApi = api
return this
}
method (action, ...middlewares) {
let newMiddlewares = middlewares.map((middleware) => {
let asyncMiddleware = this._toAsyncMiddleware(middleware)
return async (req, res, next) => {
return asyncMiddleware(req, res, next).catch(next)
}
})
// 假如要支持中间件嵌套,可以使用this._combineMiddleware来合并成一个中间件
// this.realApp.route(this.curApi)[action](this._combineMiddleware(newMiddlewares))
this.realApp.route(this.curApi)[action](newMiddlewares)
}
}
假设我们的路由注册方式是这样的:
app.route('/api/example').get((req, res, next) => next(), (req, res, next) => res.send('response'))
其中, app是express实例, 注册了/api/example的路由,使用get方法,有两个中间件。
我们使用:
let adapter = new AppRouteAdapter(app)
adapter.route('/api/example').get((req, res, next) => next(), (req, res, next) => res.send('response'))
把app换成adapter, 来进行路由注册。
那么这个adapter做了什么呢?
1. 把2个中间件都转换成async function,
2. 所有async的中间件使用.catch(next)进行统一错误捕获,并把错误传给next,让express的错误处理中间件可以进行处理。
这样,也就实现了前面koa说拥有的那些错误处理的优点。