对于Wcf的异常处理,已经有些文章讲得很好了.
基础性的文章:老徐的错误契约与异常处理.
以及Artech的扩展应用文章.
Enterprise Library深入解析与灵活应用(8):WCF与Exception Handling AppBlock集成[上]
Enterprise Library深入解析与灵活应用(8):WCF与Exception Handling AppBlock集成[下]
最近在搞个基于.net 4的开发应用.在实际的应用中Wcf自身的错误控制开关<serviceDebug includeExceptionDetailInFaults="true"/>,一般在发布时需要关闭在关闭后不利用异常的捕捉.而且传递出的并不一定是真正的错误异常.这个是错误处理改进的最大原因了.当然.不想在接口契约加上FaultContract属性也是出于开发方便的想法,我承认我懒.
异常都是通过消息传递到客户端的,在何处捕捉异常就决定了采用何种方式.所幸Wcf为我们做了很多. 错误处理接口:IErrorHandler,围绕着这个接口来实现自定义方式.
- 异常明细封装,引用Artech文章中的类定义,其中.FaultAction进行了调整,原因在后说明.代码如下:
![]()
代码
[DataContract(Namespace = " http://www.artech.com/ " )]
public class ServiceExceptionDetail : ExceptionDetail
{
public const string FaultSubCodeNamespace = @" http://www.artech.com/exceptionhandling/ " ;
public const string FaultSubCodeName = " ServiceError " ;
public const string FaultAction = @" http://www.w3.org/2005/08/addressing/soap/fault " ;
[DataMember]
public string AssemblyQualifiedName { get ; private set ; }
[DataMember]
public new ServiceExceptionDetail InnerException { get ; private set ; }
public ServiceExceptionDetail(System.Exception ex)
: base (ex)
{
this .AssemblyQualifiedName = ex.GetType().AssemblyQualifiedName;
if ( null != ex.InnerException)
{
this .InnerException = new ServiceExceptionDetail(ex.InnerException);
}
}
public override string ToString()
{
return this .Message;
}
}
- IErrorHandler实现.代码也引自Artech,代码如下:
![]()
ServiceErrorHandler
public class ServiceErrorHandler : IErrorHandler
{
public ServiceErrorHandler()
{
}
#region IErrorHandler 成员
public bool HandleError(System.Exception error)
{
return false ;
}
public void ProvideFault(System.Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault)
{
if ( typeof (FaultException).IsInstanceOfType(error))
{
return ;
}
try
{
fault = BuildFault(version, error);
}
catch (System.Exception ex)
{
fault = BuildFault(version, ex);
}
}
private Message BuildFault(System.ServiceModel.Channels.MessageVersion version,System.Exception error)
{
ServiceExceptionDetail exceptionDetail = new ServiceExceptionDetail(error);
FaultCode fc = FaultCode.CreateSenderFaultCode(ServiceExceptionDetail.FaultSubCodeName, ServiceExceptionDetail.FaultSubCodeNamespace);
FaultException < ServiceExceptionDetail > fe = new FaultException < ServiceExceptionDetail > (exceptionDetail, new FaultReason(error.Message), fc, ServiceExceptionDetail.FaultAction);
MessageFault msgfa = fe.CreateMessageFault();
return Message.CreateMessage(version, msgfa, fe.Action);
}
#endregion
}
3. 扩展服务端及客户端终结点行为,本方法比较多.可基于多种接口.我是采用了继承IEndpointBehavior接口.并配置文件配置behaviorConfiguration属性来控
制.在服务端通过ApplyDispatchBehavior方法加入自定义的消息检查和错误处理类,客户端为ApplyClientBehavior方法.
![]()
服务端
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add( new ServiceDispatchMessageInspector());
// 错误处理模块
endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add( new ServiceErrorHandler());
}![]()
客户端
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add( new ClientMessageInspector());
}4. 通过扩展终结点行为.服务端已经能对异常封装,并通过消息发送到客户端了.客户端安全检查配置会影响到消息是否能正常接收.请注意一个配置binding节点中 <security mode="None"></security>,如果该设置不要求,那恭喜你,省事了.在第1点提到的FaultAction你可随便设置.如果该设置有特殊要求,那也就是为何
FaultAction要特殊设置的原因了.我设置了其他值,结果在客户端都会得到一个 CommunicationException异常,(注:在接口契约加入 FaultContract也可解决,但我通过程序方式在ServicePiont的Fault集合加入FaultDescription,该方式也失败),而我将Action设置为代码中的值时,也就是默认 FaultException的Action值时.结果的通过了检查. 让我非常无语.我没有再去看安全性检查所涉及的内容.FaultException中Action的产生更让我担心些,不知跟哪些有关联.知道的请告诉我一下.5. 我选择在客户端自定义的消息检查类的AfterReceiveReply方法中执行检查.代码如下:![]()
代码
public void AfterReceiveReply( ref System.ServiceModel.Channels.Message reply, object correlationState)
{
if (reply.Headers.Action == ServiceExceptionDetail.FaultAction)
{
MessageFault fault = MessageFault.CreateFault(reply, int .MaxValue);
if (fault.Code.SubCode.Name == ServiceExceptionDetail.FaultSubCodeName &&
fault.Code.SubCode.Namespace == ServiceExceptionDetail.FaultSubCodeNamespace)
{
FaultException < ServiceExceptionDetail > exception = (FaultException < ServiceExceptionDetail > )FaultException.CreateFault(fault, typeof (ServiceExceptionDetail));
throw GetException(exception.Detail);
}
}
}这样.基本的一套异常处理已经完成.对于无法序列化的异常.采用的是抛出错误信息的方式.尽量保留了原错误信息.至于要记录Log.在相应的方法上补允也就可以完成了.很抱歉,没有将实例单独的代码放出来,有些限制.但主要是将在自定义异常处理中一些心得定出来罢.
转载于:https://www.cnblogs.com/platoli/archive/2011/02/18/1958016.html