使用 HttpModule 进行异常处理
我们正在审查该公司的一个系统的异常处理,并发现了一些有趣的事情。
大多数代码块(如果不是全部)都位于 try/catch 块内,并且在 catch 块内抛出一个新的 BaseApplicationException - 这似乎来自企业库。 我在这里遇到了一些麻烦,因为我看不到这样做的好处。 (每当发生异常时抛出另一个异常) 一位已经使用该系统一段时间的开发人员表示,这是因为该类负责发布异常(发送电子邮件之类的东西),但他对此不太确定。 在花了一些时间浏览代码后,我非常有信心地说,它所做的只是收集有关环境的信息,而不是发布它。
我的问题是: - 将所有代码包装在 try { } catch { } 块中而不是抛出新的异常是否合理? 如果是的话,为什么? 有什么好处?
我个人的观点是,使用 HttpModule、注册 Application 事件的 Error 事件并在模块内执行必要的操作会更容易。 如果我们沿着这条路走下去,我们会错过什么吗? 有什么缺点吗?
非常感谢您的意见。
We're reviewing one of the company's system's exception handling and found a couple of interesting things.
Most of the code blocks (if not all of them) are inside a try/catch block, and inside the catch block a new BaseApplicationException is being thrown - which seems to be coming from the Enterprise Libraries.
I'm in a bit of a trouble here as I don't see the benefits off doing this. (throwing another exception anytime one occurs)
One of the developers who's been using the system for a while said it's because that class's in charge of publishing the exception (sending emails and stuff like that) but he wasn't too sure about it.
After spending some time going through the code I'm quite confident to say, that's all it does is collecting information about the environment and than publishing it.
My question is:
- Is it reasonable to wrap all the code inside try { } catch { } blocks and than throw a new exception? And if it is, why? What's the benefit?
My personal opinion is that it would be much easier to use an HttpModule, sign up for the Error event of the Application event, and do what's necessary inside the module. If we'd go down this road, would we miss something? Any drawbacks?
Your opinion's much appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
从不1
catch(异常ex)
。 期间2。 您无法处理可能捕获的所有不同类型的错误。如果您无法处理异常派生类型或无法提供附加信息(供后续异常处理程序使用),则切勿3捕获异常派生类型。 显示错误消息不与处理错误不同。
从我的想法来看,有几个原因:
如果你知道如何处理特定异常(并将应用程序重置为错误前状态),捕获它。 (这就是为什么它被称为异常处理。)
要处理未捕获的异常,请侦听适当的事件。 在执行 WinForms 时,您需要监听 System.AppDomain.CurrentDomain.UnhandledException,并且 - 如果您执行线程处理 - System.Windows.Forms.Application .ThreadException. 对于 Web 应用程序,也有类似的机制 (
System.Web.HttpApplication.Error
)。至于在应用程序中包装框架异常(非)特定异常(即
抛出 new MyBaseException(ex);
):完全没有意义,而且味道不好。4编辑
1 从不是一个非常严厉的词,尤其是在工程方面,正如 @Chris 在评论中指出的那样。 我承认,当我第一次写这个答案时,我的原则是很高的。
2,3 参见1。
4 如果你没有带来任何新的东西,我仍然坚持这一点。 如果您在知道可能以多种方式失败的方法中捕获了
Exception ex
,那么我相信当前的方法应该在其签名中反映这一点。 如您所知,异常不是方法签名的一部分。Never1
catch (Exception ex)
. Period2. There is no way you can handle all the different kinds of errors that you may catch.Never3 catch an Exception-derived type if you can't handle it or provide additional information (to be used by subsequent exception handlers). Displaying an error message is not the same as handling the error.
A couple of reasons for this, from the top of my head:
If you know how to handle a specific exception (and reset the application to pre-error state), catch it. (That's why it's called exception handling.)
To handle exceptions that are not caught, listen for the appropriate events. When doing WinForms, you'll need to listen for
System.AppDomain.CurrentDomain.UnhandledException
, and - if your doingThreading
-System.Windows.Forms.Application.ThreadException.
For web apps, there are similar mechanisms (System.Web.HttpApplication.Error
).As for wrapping framework exceptions in your application (non-)specific exceptions (i.e.
throw new MyBaseException(ex);
): Utterly pointless, and a bad smell.4Edit
1 Never is a very harsh word, especially when it comes to engineering, as @Chris pointed out in the comments. I'll admit to being high on principles when I first wrote this answer.
2,3 See 1.
4 If you don't bring anything new to the table, I still stand by this. If you have caught
Exception ex
as part of a method that you know could fail in any number of ways, I believe that the current method should reflect that in it's signature. And as you know, exceptions is not part of the method signature.如果我正确地阅读了这个问题,我会说实现一个 try / catch 来拦截异常(你没有提到 - 它是捕获所有异常,还是只是一个特定的异常?)并抛出不同的异常通常是一件坏事。
缺点:
至少您将丢失堆栈跟踪信息 - 您将看到的堆栈只会扩展到抛出新异常的方法 - 您可能会在这里丢失一些好的调试信息。
如果您捕获异常,那么您将面临掩盖关键异常的风险,例如使用不太关键的异常来掩盖 OutOfMemory 或 StackOverflow,从而使进程继续运行,而该进程可能应该被拆除。
可能的优点:
在某些非常特定的情况下,您可以采用没有太多调试值的异常(例如从数据库返回的一些异常)并用添加更多上下文的异常进行包装,例如您正在处理的对象的ID 。
然而,在几乎所有情况下,这种气味都是难闻的,应谨慎使用。
一般来说,只有当您可以在该位置执行一些实际操作时(即恢复、回滚、执行 B 计划等)时,您才应该捕获异常。如果您对此无能为力,只需允许它沿着链向上传递即可。 仅当该位置有可用的特定且有用的数据时,您才应该捕获并抛出新的异常,这些数据可以增强原始异常,从而有助于调试。
If I am reading the question correctly, I would say that implementing a try / catch which intercept exceptions (you don't mention - is it catching all exceptions, or just a specific one?) and throws a different exception is generally a bad thing.
Disadvantages:
At the very least you will lose stack trace information - the stack you will see will only extend to the method in which the new exception is thrown - you potentially lose some good debug info here.
If you are catching Exception, you are running the risk of masking critical exceptions, like OutOfMemory or StackOverflow with a less critical exception, and thus leaving the process running, where perhaps it should have been torn down.
Possible Advantages:
In some very specific cases you could take an exception which doesn't have much debug value (like some exceptions coming back from a database) and wrap with an exception which adds more context, e.g id of the object you were dealing with.
However, in almost all cases this is a bad smell and should be used with caution.
Generally you should only catch an exception when there is something realistic that you can do in that location- ie recovering, rolling back, going to plan B etc. If there is nothing you can do about it, just allow it to pass up the chain. You should only catch and throw a new exception if there is specific and useful data available in that location which can augment the original exception and hence aid debugging.
我的思想流派应该使用 try/catch 块并且不重新抛出异常。 如果您执行的代码可能会出错,那么应该对其进行处理、记录并返回一些内容。 重新抛出异常仅用于稍后在应用程序生命周期中重新记录的目的。
这是一篇关于如何使用 HttpModule 处理异常的有趣文章: http://blogs.msdn.com/rahulso/archive/2008/07/13/how-to-use-httpmodules-to-troubleshoot- your-asp-net-application.aspx 和 http://blogs.msdn.com/rahulso/archive/2008/07/18/asp- net-how-to-write-error-messages-into-a-text-file-using-a-simple-httpmodule.aspx
I'm from the school of thought where try/ catch blocks should be used and exceptions not rethrown. If you have executing code which is likely to error then it should be handled, logged and something returned. Rethrowing the exception only serves the purpose to re-log later in the application life cycle.
Here's an interesting post on how to use a HttpModule to handle exceptions: http://blogs.msdn.com/rahulso/archive/2008/07/13/how-to-use-httpmodules-to-troubleshoot-your-asp-net-application.aspx and http://blogs.msdn.com/rahulso/archive/2008/07/18/asp-net-how-to-write-error-messages-into-a-text-file-using-a-simple-httpmodule.aspx
请查看 ELMAH。 它的作用就是你所说的。 很好。
当我创建库时,我尝试始终为调用者提供更少数量的异常以供处理。 例如,考虑连接到 SQL 数据库的存储库组件。 理论上可以抛出大量异常,从 sql 客户端异常到无效强制转换异常。 其中许多都有明确的文档记录,并且可以在编译时进行说明。 因此,我尽可能多地捕获它们,将它们放入单个异常类型(例如 RepositoryException)中,然后让该异常汇总调用堆栈。
保留了原始异常,因此可以诊断原始异常。 但我的调用者只需要担心处理单个异常类型,而不用在代码中添加大量不同的 catch 块。
当然,这也存在一些问题。 最值得注意的是,如果调用者可以处理其中一些异常,他们必须以 RepositoryException 为根,然后切换内部异常的类型来处理它。 它不如为单个异常类型使用单个 catch 块干净。 不过,我认为这不是什么大问题。
Check out ELMAH. It does what you're talking about. Very well.
When I create libraries I try to always provide a reduced number of exceptions for callers to handle. For example, think of a Repository component that connects to a sql database. There are TONS of exceptions, from sql client exceptions to invalid cast exceptions, that can theoretically be thrown. Many of these are clearly documented and can be accounted for at compile time. So, I catch as many of them as I can, place them in a single exception type, say a RepositoryException, and let that exception roll up the call stack.
The original exception is retained, so the original exception can be diagnosed. But my callers only need to worry about handling a single exception type rather than litter their code with tons of different catch blocks.
There are, of course, some issues with this. Most notably, if the caller can handle some of these exceptions, they have to root around in the RepositoryException and then switch on the type of the inner exception to handle it. Its less clean than having a single catch block for a single exception type. I don't think thats much of an issue, however.
听起来抛出的异常不应该被实现为异常。
无论如何,我想说,由于这个 BaseApplicationException 是一个通用的通用异常,因此最好抛出更特定于上下文的异常。 因此,当您尝试从数据库检索实体时,您可能需要 EntityNotFoundException。 这样,当您调试时,您不必搜索内部异常和堆栈跟踪来找到真正的问题。 如果此 BAseApplicationException 正在收集有关异常的信息(例如跟踪内部异常),那么这应该不是问题。
仅当我无法更接近代码中实际发生异常的位置时,我才会使用 HttpModule。 您并不真正想要一个 HttModule OnError 事件,它是一个巨大的 switch 语句,具体取决于 BaseApplicationException 的错误信息。
总而言之,当您可以提供更具体的异常来立即告诉您问题的根源时,抛出不同的异常是值得的。
Sounds like the exception that is thrown should not have been implemented as an exception.
Anyway, I would say that since this BaseApplicationException is a general all-purpose exception, it would be good to throw exceptions that are more context-specific. So when you are trying to retrieve an entity from a database, you might want an EntityNotFoundException. This way when you are debugging you do not have to search through inner exceptions and stack traces to find the real issue. If this BAseApplicationException is collecting information on the exception (like keeping track of the inner exception) then this should not be a problem.
I would use the HttpModule only when I could not get any closer to where the exceptions are actually happening in code. You do not really want an HttModule OnError event that is a giant switch statement depending on BaseApplicationexception's error information.
To conclude, it is worth it to throw different exceptions when you can give more specific exceptions that tell you the root of the problem right off the bat.
根据我的经验,捕获异常,将错误添加到 Server (?) 对象。 这将允许 .NET 执行它需要执行的操作,然后显示您的异常。
From my experience, catch the exception, add the error to the Server (?) object. This will allow .NET to do what ever it needs to do, then display your exception.