一个比较顺手的开发框架,我们都希望开发人员不要去太关心异常的处理,除了一个特定异常需要特定处理外,我们都希望我们很多普通的异常都能由框架来帮我们搞定。比如异常的日志记录,异常信息的提示,异常的进一步分类的判断等等,我们都不希望开发人员去使用相同或类似的代码去完成。简单来讲,我们不希望我们的代码中出现太多的try…catch 代码。如果在一个项目中,频繁出现try…catch的代码块,一方面代码不太优雅,另一方面也会给我们的代码编码带来一定的障碍,由于变量作用域(try和catch属于不同的代码块)的问题,我们很多时候不得不把变量定义在try…catch之外的代码块当中。因此,在很多业务逻辑的代码中,我们一般都不太会去专门做异常的处理,而相反,我们经常会根据业务逻辑的需要抛出很多业务逻辑异常出来,抛给UI或是业务调用者,而这种异常很多时候是希望显示给最终用户的友好信息提示。
因此,将异常的捕获,处理和显示放在UI代码中,无论从哪个角度来看都是最好的选择。结合ASP.NET MVC框架,为我们提供了的统一异常处理方案是重写Controller.OnException方法,或者是实现IExceptionFilter接口的ActionFilter。然而,这样真的就可以满足我们统一异常处理的需要吗?来看看下面的几种情况:
1.
public ActionResult Index() { var model = GetModel(); return View(model); }
在这种情况下,假设在GetModel的时候出现了异常,这时我们别无他法,即时你把异常捕获处理了,返回到Index View任何无法显示任何有用的信息。此时,我们重定向到统一友好错误页面是可以接受的。因此,在这种情况下,MVC提供的异常处理机制来统一处理这种情况下的异常就会非常简单有效。
2.
[HttpPost] public ActionResult Edit(User model) { if (ModelState.IsValid) { SaveModel(model); return RedirectToAction("Index", "Home") } return View(model); }
在这种情况下,假设我们在SaveModel时出现了异常,这个异常假设不是系统性异常,而是一个业务性异常。我们更希望是的,在用户当前操作页面上提示该异常信息,保持用户输入值不变,用户只需要根据提示消息,修改一两个输入值即可再次提交表单,而不是重定向到错误提示页面之后再返回回来完全重新输入表单。在这种要求下,MVC提供的异常处理机制就无法满足需要,因为model这种异常在统一的异常处理上下文中无法得到。因此,我们就只能添加异常处理代码块了:
[HttpPost] public ActionResult Edit(User model) { try { if (ModelState.IsValid) { SaveModel(model); return RedirectToAction("Index", "Home") } } catch (Exception e) { //log ModelState.AddModelError("", e.Message); } return View(model); }
而这样的代码,正是我们所希望避免的,然而这样的性质的Action在我们的系统中比比皆是。不过,此时在满足某种前提条件的情况下,假设我们的Model变量名都是”model”,我们是有可能通过ActionExecutingContext.ActionParameters得到我们所要的model值,提供给View。通过这种workaround的处理,我们也许可以很好的绕过此问题。我们再来看另一种情况:
3.
[HttpPost] public ActionResult Edit(User user) { if (ModelState.IsValid) { SaveModel(user); } var model = GetModel(user.Name); ViewData["Title"] = "Edit"; return View(model); }在这种情况下,也许我们除了try…catch外,真的没有其它更好的办法了。
异常处理一直都是我们软件开发过程当中,不得不去面对的麻烦。我们程序员都很害怕,因为一个个不可预期的异常而导致我们的业务模块无法正确运行,无法友好的提示给用户而招来抱怨,但是我们又不喜欢因为这些可能并不经常发生的意外而多做很多零碎事情,而且会让代码变得很丑。在一般的业务系统当中,我们更加不会加很多的catch并列句,去分门别类的处理各种各样的异常。如何更加简单,省事的把异常纳入可控的范围一直是我们努力的方向。