在数据访问层内部甚至更高层出现的错误(例如在ADO.net操作中)很少对最终用户有意义.简单地将这些错误冒泡到UI并显示它们通常除了对最终用户的挫败之外什么都不会实现.
我最近采用了一种基本技术来报告这样的错误,我抓住了错误并至少添加了一些用户友好的文本,以便至少最终用户能够理解失败的原因.
为此,我在每个特定函数中捕获异常(例如,数据访问层中的获取函数),然后使用用户友好的文本引发一个新错误,该文本关于失败并可能导致的函数,但随后嵌入原始函数新异常中的异常是该新异常的"内部异常".
如果需要,这可以在每一层发生,较低级别的每个消费者将其自己的上下文添加到错误消息中,以便到达UI的是越来越用户友好的错误消息.
一旦错误到达UI - 如果有必要 - 它可以迭代嵌套的异常,以显示错误消息,首先告诉用户哪个操作失败,但也提供了一些关于实际出错的技术信息.
例如
"无法显示您请求的客户名称列表."
"由于数据库出错,获取您请求的客户列表失败."
"在检索客户列表时连接到数据库时出错"
"用户xx登录失败"
我的问题是:这是非常低效的(所有那些嵌套的例外)?我怀疑这不是最好的做法,所以我应该做些什么才能达到同样的目的 - 或者我实际上应该尝试做些更好的事情?
这有点可怕.
如果您向最终用户显示错误,则该用户应该能够对其采取行动.在"您所请求的客户名称列表中无法显示".例如,您的用户只会想"那么什么?" 在所有这些情况下,只显示"发生了一些不好的事"消息.你甚至不需要捕获这些异常,当出现问题时,让一些全局方法(如application_error)处理它并显示一般消息.当您或您的用户可以对错误采取措施时,请抓住它并执行操作或通知用户.
但是您需要记录您未处理的每个错误.
顺便说一句,显示有关发生的错误的信息可能会产生安全漏洞.攻击者对你的系统了解得越少,就越不可能找到破解它的方法(记住那些消息,比如"sql语句中的语法错误:选择*来自用户名='a'; drp数据库; - '.. "预期:'下降'而不是'drp'.他们不再制作这样的网站了.
抛出新的例外在技术上是昂贵的,但是我不会对此进行大的辩论,因为"代价高昂"是相对的 - 如果你每分钟抛出100个这样的例外,你可能不会看到成本; 如果你每秒抛出1000个这样的异常,你很可能会看到性能受到影响(因此,这里不值得讨论 - 性能就是你的呼唤).
我想我不得不问为什么要使用这种方法.是否真的可以在可能抛出异常的每个级别添加有意义的异常信息,如果是这样,那么信息也是如此:
您实际想与用户分享的内容?
您的用户能够解释,理解和使用的东西吗?
以这样的方式编写,它不会干扰以后重用低级组件,在编写它们时可能不知道它的实用性?
我询问与您的用户共享信息,因为在您的示例中,您的人工堆栈首先通知用户在数据库上进行身份验证时出现问题.对于潜在的黑客来说,这是一个很好的信息,可以揭示操作正在做什么.
至于回传整个自定义异常堆栈,我认为这对大多数(诚实)用户来说都不会有用.例如,如果我在获取客户名单时遇到问题,是否会帮助我(作为用户)知道在使用数据库进行身份验证时出现问题?除非您使用集成身份验证,并且每个用户都有一个帐户,并且能够联系系统管理员以找出他们的帐户缺少权限的原因,可能不会.
首先,我要确定抛出的Framework异常与您要向用户提供的异常消息之间是否存在语义差异.如果有,则继续在最低级别使用自定义异常(在您的示例中为"登录失败").之后的步骤,直到异常的实际呈现,并不真正需要任何自定义异常.您感兴趣的例外已经生成(登录失败) - 继续在调用堆栈的每个级别包装该消息除了向您的用户公开您的调用堆栈之外没有任何其他用途.对于那些"中间"步骤,假设有任何try/catch块,一个简单的"log and throw"策略可以正常工作.
但实际上,这种策略还有另一个潜在的缺陷:它迫使开发人员负责维护已实施的自定义异常标准.因为在编写低级类型时他们不可能知道调用层次结构的每个排列(他们的"客户端"甚至可能还没有写过),所以所有开发人员 - 甚至一个开发人员 - 似乎都不记得包装和定制每个代码块中的任何错误条件.
我通常担心在过程的后期尽可能显示抛出的异常(即尽可能接近调用堆栈的"顶部"),而不是自下而上.通常,我不会尝试替换在我的应用程序的低级别抛出的异常中的任何消息 - 特别是因为这些低级别成员的使用往往越来越抽象,调用越深入.我倾向于捕获并记录业务层中的异常并降低,然后处理在表示层中以易于理解的方式显示它们.
以下是关于异常处理最佳实践的几篇不错的文章:
http://www.codeproject.com/KB/architecture/exceptionbestpractices.aspx
http://aspalliance.com/1119
Jeez这是罗嗦...提前道歉.