Wcf自定义错误处理机制

对于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,围绕着这个接口来实现自定义方式.

  1. 异常明细封装,引用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 { getprivate set; }

            [DataMember]
            
    public new ServiceExceptionDetail InnerException { getprivate 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;
            }
        }


  2. 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.在相应的方法上补允也就可以完成了.很抱歉,没有将实例单独的代码放出来,有些限制.但主要是将在自定义异常处理中一些心得定出来罢.

 

posted @ 2011-02-18 17:48  AddOnMe  阅读(646)  评论(0编辑  收藏  举报