起因
在之前的项目中我发现每个控制器大约都是这样写的
async function findId (id) {
let res;
try{
res = await document.findById(id);
}catch(e){
return false;
}
}
当然这么写其实没有任何问题,甚至是一个很好的习惯,他保证了错误可以被正常的捕获,另外可以在catch里随便做处理,比如console,错误日志。一切都看起来那么完美
转折
那么假设现在有一个开发就是那么刚,写了几十个控制器,然后都没加try catch,理直气壮的说,这么多重复代码真的不能抽象一下统一处理吗。
好吧且不说抽象统一处理的事,解决问题才是目的。
解决吧,他刚我也刚,每个控制器加上不就是了。。。呵呵哒,边骂边加,我想半天也差不多了。
这样真的好吗,万一现在在catch这需要根据e做一些特殊处理呢或者增加一个功能呢?egg的话有一个统一的errorhandle可以配置。确实把整个错误处理可以完整的抽象出来。
面向切面编程
这里的话要提一个java大大朋友告诉我的spring的解决手段,面向切面编程,具体概念网上解释很多,大约可以理解为,在所有控制器前,或者说控制器和服务之间,这样的话就可以实现很多公共逻辑的抽象。
面向切面编程适合做的事,这个spring的实践实在太多我就不赘述了,错误异常处理,日志,权限,这些在切面上的实践都是很好的。
切面与中间件
其实中间件我认为也属于一个切面,这个切面是在请求上下文中的,但是这个切面无法覆盖所有的要求,特别当你需要控制的功能不是以请求为粒度控制的时候,或者说仅仅需要对服务层的服务添加功能的时候。
实践
这里就以开头的那段代码的简化为例。首先是一个包装函数,用来包装所有的控制器的方法
function controllerWrap(controllerFn, self) {
return async function() {
let that = self;
try {
await controllerFn.apply(that, arguments);
} catch(e) {
throw(e);
}
};
}
其实注意点也就两个,注意上下文,然后是保证controllerFn的arguments不要丢了,其实也就是ctx和next的
在把所有的控制器实例化后(一般在都会把控制器形成单例模式,之后只要遍历这些单例中的方法包裹控制器函数就可以了)。当然可以在wrap中注入更多统一处理函数,也可以通过函数名做一些判断,比如对所有名字中包含save的函数进行单独的日志处理。
egg.js的实践
egg在实践上是不太一致,因为控制器并非简单的实例化,如果需要对egg的控制器的进行包装则需要在loader中进行包装。
参考:https://eggjs.org/zh-cn/advan...
之后会尝试根据现有的业务沉淀出一套egg之上的框架。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。