处理WCF异常的方式_.NET_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > .NET > 处理WCF异常的方式

处理WCF异常的方式

 2013/10/20 9:47:35  fhuan123  程序员俱乐部  我要评论(0)
  • 摘要:任何程序都离不开对异常的处理,良好的异常处理方式可加快寻找出异常的根源,同时也需要避免暴露敏感信息到异常中。WCF这种典型的服务端和客户端交互的程序,服务端的异常更需要适当的处理。下面以一个简单的服务为例,说明WCF中处理异常的方式。WCF服务定义如下,很明显方法Divide在divisor为0的时候将会抛出异常ViewCodepublicclassCalculateService:ICalculateService{publicintDivide(intdividend,intdivisor
  • 标签:方式 WCF 异常

任何程序都离不开对异常的处理,良好的异常处理方式可加快寻找出异常的根源,同时也需要避免暴露敏感信息到异常中。WCF这种典型的服务端和客户端交互的程序,服务端的异常更需要适当的处理。下面以一个简单的服务为例,说明WCF中处理异常的方式。

WCF服务定义如下,很明显方法Divide在divisor为0的时候将会抛出异常

class="code_img_opened" style="display: inline;" src="/Upload/Images/2013102009/2B1B950FA3DF188F.gif">logs_code_collapse">View Code 复制代码 public?class?CalculateService?:?ICalculateService
????{
????????public?int?Divide(int?dividend,?int?divisor)
????????{
????????????return?dividend?/?divisor;
????????}

????????public?int?Add(int?a,?int?b)
????????{
????????????return?a?+?b;
????????}
????} 复制代码

客户端调用如下:

View Code 复制代码 ?using?(var?client?=?new?CalculateServiceClient())
????????????{
????????????????try
????????????????{
????????????????????Console.WriteLine(client.Divide(20,?0));
????????????????}
????????????????catch?(FaultException?ex)
????????????????{
????????????????????Console.WriteLine(ex.Reason);
????????????????}

??????????????} 复制代码

首先需要知道的是,WCF的异常信息默认是以FaultException的形式返回到客户端,FaultException的关键属性 Reason是对客户端反馈的最重要信息之一。以上客户端代码调用之后,默认的FaultException返回的Message信息如下:

由于内部错误,服务器无法处理该请求。有关该错误的详细信息,请 打开服务器上的 IncludeExceptionDetailInFaults (从 ServiceBehaviorAttribute 或从 <serviceDebug> 配置行为)以便将异常信息发送回客户端,或在打开每个 Microsoft .NET Framework 3.0 SDK 文档的跟踪的同时检查服务器跟踪日志。

根据异常的提示,意思说如果要在客户端看到详细的Exception信息,那么请将ServiceBehavior对应的IncludeExceptionDetailInFaults属性设置为True,通常在配置中表现为如下设置:

View Code 复制代码 1?<serviceBehaviors>
2?????????<behavior>
3???????????<serviceMetadata?httpGetEnabled="True"?httpGetUrl="http://localhost:8733/CalculateService/"/>
4?????????? <serviceDebug?includeExceptionDetailInFaults="True"?/>
5?????????</behavior>
6???????</serviceBehaviors> 复制代码

通过以上设置之后,客户端输出的内容为“尝试除以零”,这个提示信息跟原始的异常信息是一致,即返回的FaultException中的 Reason包含原始异常的Message的值,但是这样处理之后服务端所报出的异常信息直接传到了客户端,比如一些保密信息也可能输出到了客户端,因此 对于异常信息必须进行一个封装。最直接的形式莫过于在服务端就把异常给捕获了,并重新throw一个FaultException

服务端的代码改进如下,经过以下改进,那么客户端得到的信息仅仅是"操作失败",同时服务端也记录了异常信息(这时IncludeExceptionDetailInFaults是设置为False的)。

View Code 复制代码 1?try
2?????????????{
3?????????????????return?dividend?/?divisor;
4?????????????}
5?????????????catch?(Exception?ex)
6?????????????{
7?????????????????Console.WriteLine(ex.Message);
8?????????????????throw?new?FaultException("操作失败");
9?????????????} 复制代码

当然这是FaultException的默认用法,FaultException还支持强类型的异常错误信息,返回更加丰富和精确的错误提示。假设 定义如下通用的一个FaultContract类型,将出错时的用户名和线程名字记录到异常信息中,因为异常信息也是通过SOAP格式传输的,因此跟定义 其他DataContract的方式一样。

CommonFaultContract 复制代码 1?????[DataContract]
2?????public?class?CommonFaultContract
3?????{
4?????????[DataMember]
5?????????public?string?UserName?{?get;?set;?}
6?????????[DataMember]
7?????????public?string??ThreadName?{?get;?set;?}
8?????} 复制代码

那么服务方法的接口需要增加如下标记,如果不这样标记,那么客户端得到的异常类型依然是FaultException,而不是强类型的异常信息。

?[FaultContract(typeof(CommonFaultContract))]
?int Divide(int dividend, int divisor)

实现方法中抛出异常的部分代码改成如下:

异常处理 复制代码 1?catch?(Exception?ex)
2?????????????{
3?????????????????Console.WriteLine(ex.Message);
4?????????????????throw?new?FaultException<CommonFaultContract>(new?CommonFaultContract?
5?????????????????{
6?????????????????????UserName?=?Environment.UserName,
7?????????????????????ThreadName?=?System.Threading.Thread.CurrentThread.Name
8?????????????????},?"操作失败");
9?????????????} 复制代码

?这时候重新生成客户端的代理类,然后更新客户端的代码如下,红色部分即获取强类型的异常错误信息。

View Code 复制代码 ?1?try
?2?????????????????{
?3?????????????????????Console.WriteLine(client.Divide(20,?0));
?4?????????????????}
?5?????????????????catch?(FaultException<CommonFaultContract>?ex)
?6?????????????????{
?7?????????????????????Console.WriteLine(ex.Detail.ThreadName);
?8?????????????????????Console.WriteLine(ex.Detail.UserName);
?9?????????????????????Console.WriteLine(ex.Reason);
10?????????????????} 复制代码

当然在具体应用中还需要根据需求,返回不同的信息,构建不同的FaultContract。

  以上服务端捕获的异常方法,适用于方法比较少的情况,如果有十多个方法,一个个去写try catch然后做标记等,那么工作量会很大,而且代码也不利于重用。尝试寻找像MVC Controller那样的统一处理Exception的方式,将异常处理都放在基类中,那么只要继承与这个基类的方法都不需要去写try catch去捕获异常。但WCF中似乎没有这样的机制,放弃了这种做法。

  最近在研究Enterprise Lib中对WCF的支持时,发现Exception Block中还特地有针对WCF程序异常处理的解决方案,而且满足以上说道的需求,即可记录异常,又可对异常信息进行封装。更重要的时,自动处理运行时的 异常信息,不需要挨个方法的去写Try catch。秉承企业库的优秀传统,大部分工作还是通过配置就可以完成了,非常好的解决方案。下面介绍具体的使用步骤。

步骤一:

引用以下dll

Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.dll

Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF.dll

Microsoft.Practices.EnterpriseLibrary.Common.dll

Microsoft.Practices.ObjectBuilder2.dll

步骤2:

在具体的实现类中,增加如下属性标记,其中WcfException为企业库中Exception Block中的一个异常处理策略,具体如何配置异常处理策略,请参考企业库的帮助文档。

[ExceptionShielding("WcfException")]
public class CalculateService : ICalculateService

那么只要增加了[ExceptionShielding("WcfException")]这个属性标记之后,所有运行时的异常都将交给策略名为WcfException的异常处理block来处理,在这里就可以执行一些异常记录以及异常封装的操作。

步骤3:

将异常信息封装为FaultException,这个动作也是通过配置来完成。在Exception节点中添加一个Fault Contract Exception Handler。

Fault Contract Exception Handler需要设置以下两个属性值

exceptionMessage:所有异常封装后的错误信息

faultContractType:即返回异常的faltContract类型,这个类型必须指定一个,哪怕方法中没有用到也要,如果方法中有用 到,那么客户端那边就能得到强类型FaultException,否则就是普通的FaultException。这里指定为之前定义的CommonFaultContract

对于faultContract类型的值,还可以通过PropertyMappings自定义需要从原始异常信息中映射到faultContract的属性中,这个属性可选。

  经过以上步骤配置之后,服务端的程序就具备了自动处理异常的功能。客户端还是跟往常那样调用,不过具体是用FaultException捕获异 常还是FaultException<T>去捕获异常,还得根据定义方法中是否标记了FaultContract。之后若定义了其他服务接 口,同样也仅仅需要在实现类上加上[ExceptionShielding("WcfException")]标记即可。

发表评论
用户名: 匿名