笔者之前分析了如何实现js的责任链异常处理的方法,通过promise这个异步模型,我们能够对同步方法和异步方法的两种情况,均可以实现责任链模式。有了这些武器,我们就可以开始设计ui的统一异常处理方案了。
1.统一异常处理方案
这里所谓统一异常处理方案,其实就是指对那些底层无法处理的,一层层抛到了边界类的异常,在边界类中根据异常的不同类型,做出不同处理方案的处理策略。为了能在边界类中对异常类型做出判断,我们需要将常用的异常类型定义出来,再将原始异常包装为这些系统内部定义的异常类型。所以,整个统一处理方案的需求大致有如下几点:
- 异常处理必须是责任链模式,最终将底层不能处理的异常抛到边界类上。
- 需要对原始异常包装系统异常,将其封装为我们指定的系统。
- 系统异常封装过程需要业务代码解耦,方便以后对其扩展。
- 边界类中的统一异常处理能够对与不同的系统异常,需要采用的不同的处理策略。
- 系统异常和处理策略是可增加的。
- 系统异常必须包含错误的堆栈调用信息,方便调试时找出错误。
这种统一处理异常的方案,我们在服务器端开发经常用到的,客户端往往不需要实现这种复杂的异常处理方案。但是随着前后台完全解耦,尤其是spa应用的但是,前端现在越来越复杂。而系统复杂的同时,出现异常的概率和异常处理的难度也在增加,所以统一异常处理同样适于用客户端,即我们的ui页面。
2.统一的ui异常处理方案
如果后端的异常处理核心是记录日志,那么ui的异常处理核心在于向用户响应。不同的错误给用户的响应也应该是不一样的,我们需要分析出常出现的异常,并为其设计出处理策略,这才是ui异常处理方案的核心。
对于客户端(即ui)来说,通常是采取mvc模式设计的,或者是近来开始流行的mvvm。无论是哪一种,都有controllor的概念,controllor注意需要完成两个任务:
1.初始化ui:这个过程一旦出现异常,整个ui的业务逻辑都无法正常进行。处理起来往往比较棘手,通常是返回之前的ui或者是直接退出系统都有可能。
2.响应视图控件的事件:通常在事件中,通过调用底层提供的service方法,和后台服务器异步请求,或者本身就是完成一个前端交互动作(例如点击按钮,弹出一个对话框)。如果是前端交互动作通常是一个事件衔接另一个事件,一旦一个事件出现异常往往很难难有合适的处理方案。但是,如果把这些事件看成是一个异步方法就衔接起来就简单多了,只在第一个用户事件中视为调用的发起者,后续操作均看成是异步调用。
所以,需要统一做异常处理的地方,就是controllor执行页面初始化的过程,以及controllor对一个用户用例过程中的第一个事件。
同时我们还需分析一下我们需要封装哪些系统异常,因为我们需要根据其类型做出不同策略的异常处理方案的。常见的系统异常应有如下几种:
异常名称 | 异常描述 | 处理方案 |
初始化异常 | 初始化过程中出现的异常 |
异常需要告诉系统他的严重级别,因此初始化过程中一旦发生异常,可能并不影响其他模块,可能需要返回上一级页面,可能整个系统都不能再正常运。根据这种异常的严重程度,统一异常处理才能使用不同的处理策略。 |
用户取消异常 |
在一个完整的交互用例中,用户取消了继续完成此交互过程而发生的异常 |
这种异常我们开发时候非常常见,例如用用点击新建“按钮”,跳转到新建页面,填写了若干信息后点击又“取消”按钮,这时后续的一些列业务(如表单校验、提交)都应该终止,同时应该返回之前的页面。所以对于这种异常,处理方案就是返回初始装态,然后什么也不做。 |
网络异常 | 在向服务器请求过程中出现的异常 | 对于这种异常给出一个网络请求错误的提示即可,当然如果是移动的设备,也可以给出跳转打到打开网络设置的界面的连接,让用户重新设置网络配置。 |
服务器端异常 | 向服务器请求的时候,服务器端发生错误,不能正常完成相应,也就是500或者404错误 | 可能需要根据错误码做出不同的错误处理方案。需要注意的是服务器端的错误相应可能会携带错误的提示语句,所以统一异常处理需要能够将这种提示语句提示给用户 |
表单异常 | 用户填写表单时候,出现的校验错误 | 表单校验错误往往是属于视图层的事情,controllor往往不会处理这种错误。但是有一些复杂的校验工作,需要controllor配合完成,如果controllor校验失败,需要将错误传递给视图层,再由视图层去完成表单的错误提示。所以这个错误需要异常统一处理能够让视图可以配置视图自己的提示方案。 |
... | 用据业务或客户端本身需求而设计出的异常 | 这种异常是开发人员动态扩展的异常,统一异常处理允许用户注册新的处理异常和处理策略 |
基本上这些异常便可以满足绝大多数客户端的需求。
3.和客户端应用的集成
统一异常处理从他的复杂程度就能看出,只用复杂的应用才需要使用这种方案,因此我们的页面必须是富客户端的应用,最好是spa的应用。spa应用指的是单页 Web 应用 (single-page application) ,即不会因为用户的操作而进行页面的重新加载或跳转,而是利用 JavaScript 动态的变换HTML的内容,从而实现UI与用户的交互。由于避免了页面的重新加载的web页面,这种应用的复杂程度远高于传统web页面。所以这种应用最适合采用统一的异常处理方案。
同时应用也应该是采取mvc(mvvm)的设计模式,能够将视图逻辑和系统业务很好的分离开来。另外系统的业务部分以及和服务器请求的部分尽量从controllor中分离出来,封装成service层,这样可以更加增加维护性并且可以实现复用,相同业务的代码尽量只写一遍。如果没有统一异常处理方案,这种分层的异常处理实现起来是非常困难的,而使用了统一异常处理,每一层只需要考虑他们自己应该做的事情,实现这种前端分层成了可能。
同时因为异步方法的存在,所以我们的service需要是使用promise将其封装的,每个方法最好通过名称就能看出他是异步的还是同步的。当一个方法我们无法确定他是异步还是同步的时候,我们应优先用promise封装,因为同步代码转变为异步比较容易,而异步代码是无法转变为同步代码的。
最后就是具体设计和代码实现了,请见下一章节。