原文网址:http://www.uml.org.cn/j2ee/201109143.asp
在Web程序开发设计当中,总离不开系统异常报错、错误提示、规则验证等情况,不同的人,解决的办法也多种多样,这里我列举一下常见做法:
1 无任何提示
2 在jsp页面里直接:alert(‘操作失败');
3 在服务器控制台里system.out.println('插入数据失败!');
4 后台throw new RunTimeException("保存失败");
5 跳转到500.jsp 显示操作错误
上面5种做法,我觉得都有比较欠缺的地方,下面我做一下具体解释:
1 任何一个成功的项目,这样的用户体验直接让人呕吐,用产品这个词去称呼这个系统简直是对产品这两个字的侮辱,点了没反应人家还以为你在画原型呢!
2 在jsp页面里alert,这个稍微进步一点,但还是不能正确引导客户,到底是报价时间未到,还是价格报的太高? 没有具体的业务说明,不解的客户立马拿起电话往你们客服那打, 让客服伺候客户吧。然后呢,这个问题一般也不会当做bug去修改。。。
3 这种是典型的“程序员”风格报错方法。写这个代码的人还停留在“学院派”,也就是刚毕业时的那种编码调试习惯,完全不用debug调试,完全printf起家,,, 你在这边跟他说:“我流程走不下去啦”, 这是他会不忙不忙解释:“你等等,我去看看控制台。噢,明白啦,你还没操作XXX呢”,无语了,这系统是做给客户用呢还是,,,
4 能写if(true) throw new 异常类 代码的程序员,恭喜您,已经成功脱离菜鸟级别了,对代码、设计、产品体验有一定的认识,但是由于种种原因,无法完美地把这个业务提示友好地传递到前台显示,后面我将对这个问题进行重点的阐述。
5 最标准的做法,这种做法一般能解决大部分问题,但是面对当前ajax大行其道的今天,到是靠跳转页面去提示错误,也不是一个完整的解决方案。
下面我给出自己设计异常的一些经验,在这里,我不想光谈空洞的理论,所有会牵扯到一些技术、框架,前台框架技术可以触类旁通。
解决异常我们一般分几步走:
1 设计异常
2 在合适的地方抛出异常
3 在合适的地方显示异常提示
4 如何“自动”提示异常错误而不需要在jsp里手工编写显示异常代码(这里需要整合前后台框架并修改源码)
第1个问题解决方案 异常的设计:
由于文章主题是J2EE异常设计,这里我不会深入讨论 已检查异常、未检查异常这些老生常谈的话题。
我的做法是,设计好异常类。先设计一个异常超类
public class WsdException extends RuntimeException {
/** serialVersionUID */
private static final long serialVersionUID = 1L;
/** DEFAULT_LAYOUT 默认的异常tiles模板*/
public static final String DEFAULT_LAYOUT="baseSideLayout";
private String layout=DEFAULT_LAYOUT;public WsdException(String msg) {
super(msg);
}
public WsdException(String msg, String layout) {
super(msg);
this.layout=layout;
}
layout这个属性的用途后面再做解释,先记住有这么个东西。
然后各个业务模板继承这个WsdException 类, 在抛出业务代码异常的时候可以写 throw new BargainException("您的报价过高!"), 有些人烦恼异常类应该放到那个包路径下面
是放 com.aa.bb.exception还是放com.aa.bb.action下面还是com.aa.bb.domain下面呢? 我个人偏好于放在跟抛出这个异常所在的service同级, 比如BargainException可以放到 com.aa.bb.service目录下面,
例如spring源码里的就是这样的规则放置异常类的,不必单独起一个目录去放异常类,除非是你的核心包的常见异常基类。
第2个问题 异常设计:如何在合适的地方抛出异常?
在这里,正常的操作成功内容提示我就不讲了,也没必要讲,这里考虑的是错误失败的时候如何处理, 抛出异常显然在后台java代码,条件不成立的时候显示地抛出运行时异常。例如
if((select count(id) from user)==0
throw new UserException("用户不存在!");
这样子抛出的异常是一种比较优雅的方式, 如何检测空指针异常,插入字段过大等异常处理,第3,4步我在细讲。'
第3个问题 在合适的地方显示异常提示?
在ajax大行其道的今天,通过跳转到500页面显示错误提示,显示不大显示。 你也许会说:“很简单啊,我不会在ajax的回调方法里 alert(json) 吗?”, 这个不是个问题。 您说的没错, 别急,先听我慢慢分析:
假设您这个ajax是想查询用户列表数据, 假设这个功能正常调用, 回调函数 function(json) json里的值就是一个用户数组, 但假设这个ajax请求调用失败了, 会发生什么? 也许是数据库连接失败, 也许是权限不足,
您那个"alert(json)" 还抵用吗? 这个json参数,到底是一个数组还是一个错误提示串还是未知, 也许你跟我说我可以写 typeof(json) if...else alert('json"); 这样做并没有错。 您能想到这么多,很难得,说明
您已经是个web开发老手了, 根本不用担心别人说你写的功能bug有多少, 系统页面多了,功能多了,您能保证大部分coder都是刚毕业的人写的吗? 现在中国浮躁的IT大环境是:会写代码的人少些或不写,不会写代码的
拼命瞎写, 我不敢保证每位仁兄都会有语句 if(判断json类型) 如何如何, 这个很容易遗漏, 而且也不会写, 鬼知道您是同步还是异步调用?
如果是引用相同的页面, 有的是 $().load() 有的是 include jsp 有的是iframe, 有的是弹出窗口, 也有可能是同步、异步请求都会用, 这时你如何智能地判断并提示? 当然能做到:同步请求跳到错误500.jsp,
异步请求能自动提示alert(“后台抛出的异常错误提示”),那该多好啊, 记住, 我说的是自动提示, 不是你在前台还去写这个提示代码, 很多时候,很多新手都会忘记这个alert, 通常项目一急, 就会忘记这个事情。
第4个问题 如何“自动”提示异常错误而不需要在jsp里手工编写显示异常代码
问题4就是解决问题3的办法, 首先既要解决异常信息在前台异步调用的时候通过框架代码自动去提示, 又要能智能判断到底浏览器是同步还是异步请求呢?
同步请求是 把错误放到bean里面, 异步请求是输出 json格式的数据,例如 {success:false,.faile:'true','result':'数据库连接失败'}.
下面是我的解决办法:
4.1 使用spring的SimpleMappingExceptionResolver类统一解决异常,看看DispacherServlet里就知道啦, spring mvc在请求过程中会有一个统一的异常解析器, 我们可以写一个自己的异常解析器,判断一下,
如果是同步请求,就返回一个error.jsp的ModelAndView, 如果是一个异步请求, 我们则打出json格式的字符串, 而非返回一个错误页面, 如何判断同步异步,看看request里的head信息就知道啦。
也许有人会问:我用jquery的 $(div).load('url.do") 这个返回的是一个页面, 严格来讲, 你不能把他当做异步, 因为它是load一个页面来填充一个div, 这个时候你可以通过$(div).load('url.do?isAjax=true")的方式
来解决, 当然 $.getJSON $.ajax 这种典型的异步就不在话下拉。
问题又来了, 我异常是抛出了呀, 异步调用,也用自己的异常解析器打出了json串, 那前台不alert, 他哪会自动提示? 呵呵, 问题的关键在于: 你写异步调用,总会用到ajax框架吧?这年头别跟我说还写原生的
xmlhttprequet, 不管你用的是ptototype ext jquery motools , 您都可以在 ajax框架的源码里做一点小动作: ajax请求源码里有一个回调函数, 你后台返回了一个{success:false,'result':'数据库连接失败'}., 我们可以在
框架的源码里 if(json.faile=='true') , 然后就alert(json.result) 就行啦, 自己的jsp里不需要手写错误提示, 这样子多好, (推荐jquery.message), 这样提示比alert友好一些.
不用自己在前天写alert提示多好啊, 不管后台是报数据库字段找不到还是保存文件失败, 都可以“自动地提示啦!
最后本文为对layout做解释, 有兴趣的朋友加以讨论, 则个是在做tiles模板的时候用的,弹出页面保存总改换个模板名吧? 呵呵