zoukankan      html  css  js  c++  java
  • 利用Attribute和IErrorHandler处理WCF全局异常

    在处理WCF异常的时候,有大概几种方式:

    第一种是在配置文件中,将includeExceptionDetailInFaults设置为true

    <behavior name="serviceDebuBehavior"><serviceDebug includeExceptionDetailInFaults="true" /></behavior>
    但是这种方式会导致敏感信息泄漏的危险,一般我们仅仅在调试的时候才开启该属性,如果已经发布,为了安全,我们一般会设置成false。

    第二种方法是自定义错误,通过FaultException直接指定错误信息。

    [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
    public class CalculatorService : ICalculator
    {
    throw new FaultException("被除数y不能为零!");
    }
    但这样我们还必须为WCF的方法里显式的去抛出异常,如果能像.NET一样,在Global里直接捕获全局的异常,并写入log4net日志多好,有没有方法在wcf里如果出现异常,异常日志写入服务器断,同时抛给客户端呢?

    要实现这个,需要三步

    第一步: 我们需要实现IErrorHandler接口,实现他的两个方法

    bool HandleError(Exception error); void ProvideFault(Exception error, MessageVersion version, ref Message fault);

    其中HandleError是true表示终止当前session

    在ProvideFault方法里我们可以写我们要抛给客户端的自定义的错误,同时也可以在服务器端写异常日志。

    我们实现一个GlobalExceptionHandler ,继承自IErrorHandler,同时在ProvideFault里通过log4net写服务器端日志,如下:

    namespace WcfService

    {     using System;     using System.ServiceModel;     using System.ServiceModel.Channels;     using System.ServiceModel.Dispatcher;

        /// <summary>     /// GlobalExceptionHandler     /// </summary>     public class GlobalExceptionHandler : IErrorHandler     {         /// <summary>         /// 测试log4net         /// </summary>         private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(GlobalExceptionHandler));

            #region IErrorHandler Members         /// <summary>         /// HandleError         /// </summary>         /// <param name="ex">ex</param>         /// <returns>true</returns>         public bool HandleError(Exception ex)         {             return true;         }

            /// <summary>         /// ProvideFault         /// </summary>         /// <param name="ex">ex</param>         /// <param name="version">version</param>         /// <param name="msg">msg</param>         public void ProvideFault(Exception ex, MessageVersion version, ref Message msg)         {             //// 写入log4net             log.Error("WCF异常", ex);             var newEx = new FaultException(string.Format("WCF接口出错 {0}", ex.TargetSite.Name));             MessageFault msgFault = newEx.CreateMessageFault();             msg = Message.CreateMessage(version, msgFault, newEx.Action);         }         #endregion     } }

    第二步:现在我们需要创建一个自定义的Service Behaviour Attribute,让WCF知道当WCF任何异常发生的时候,我们通过这个自定义的Attribute来处理。实现这个需要继承IServiceBehavior接口,并在此类的构造函数里,我们获取到错误类型。

    一旦ApplyDispatchBehavior行为被调用时,我们通过Activator.CreateInstance创建错误handler,并把这个错误添加到每个channelDispatcher中。

    ApplyDispatchBehavior

    channelDispatcher中文叫信道分发器,当我们的ServiceHost调用Open方法,WCF就会创建我们的多个信道分发器(ChannelDispatcher),每个ChannelDispatcher都会拥有一个信道监听器(ChannelListener),ChannelListener就有一直在固定的端口监听,等到Message的到来,调用AcceptChannel构建信道形成信道栈,开始对Message的处理。

    using System;
    using System.Collections.ObjectModel;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;
     
    namespace WcfService
    {
        public class GlobalExceptionHandlerBehaviourAttribute : Attribute, IServiceBehavior
        {
            private readonly Type _errorHandlerType;
     
            public GlobalExceptionHandlerBehaviourAttribute(Type errorHandlerType)
            {
                _errorHandlerType = errorHandlerType;
            }
     
            #region IServiceBehavior Members
     
            public void Validate(ServiceDescription description,
                                 ServiceHostBase serviceHostBase)
            {
            }
     
            public void AddBindingParameters(ServiceDescription description,
                                             ServiceHostBase serviceHostBase,
                                             Collection<ServiceEndpoint> endpoints,
                                             BindingParameterCollection parameters)
            {
            }
     
            public void ApplyDispatchBehavior(ServiceDescription description,
                                              ServiceHostBase serviceHostBase)
            {
                var handler =
                    (IErrorHandler) Activator.CreateInstance(_errorHandlerType);
     
                foreach (ChannelDispatcherBase dispatcherBase in 
                    serviceHostBase.ChannelDispatchers)
                {
                    var channelDispatcher = dispatcherBase as ChannelDispatcher;
                    if (channelDispatcher != null)
                        channelDispatcher.ErrorHandlers.Add(handler);
                }
            }
     
            #endregion
        }
    }

    第三步:在我们的WCF的类上,加上GlobalExceptionHandlerBehaviour

    using System;
     
    namespace WcfService
    {
        [GlobalExceptionHandlerBehaviour(typeof (GlobalExceptionHandler))]
        public class SomeService : ISomeService
        {
            #region ISomeService Members
     
            public string SomeFailingOperation()
            {
                throw new Exception("Kaboom");
                return null;
            }
     
            #endregion
        }
    }
  • 相关阅读:
    为什么switch里的case没有break不行
    CLS的探索:Python如何让日志免费云化
    做一次“黑客“,入侵一次自己的服务器
    斥资288买了三年服务器之后应该如何配置
    Scrapy入门到放弃01:开启爬虫2.0时代
    c#自制抽奖小程序
    c#中的几种Dialog
    解决数据库排序空值排在前问题
    Oracle 数据库添加定时事件
    FileReader 对象实现图片预览
  • 原文地址:https://www.cnblogs.com/wangjingblogs/p/4699455.html
Copyright © 2011-2022 走看看