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
        }
    }
    这时候,客户端和服务器端都已经分别能记录到错误的异常日志了。
    附:log4net配置

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
    <configSections>
      <section name="log4net"
                type="log4net.Config.Log4NetConfigurationSectionHandler, log4net, Version=1.2.10.0, Culture=Neutral, PublicKeyToken=bf100aa01a5c2784" />
    </configSections>
     
    <log4net>
      <!-- 日志文件部分log输出格式的设定 -->
      <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
        <file value="./Logs\Log_" />
        <appendToFile value="true" />
        <rollingStyle value="Date" />
        <datePattern value="yyyyMMdd'.txt'" />
        <staticLogFileName value="false" />
        <layout type="log4net.Layout.PatternLayout">
          <header value="------------------------------------------------------------&#xD;&#xA;" />
          <ConversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline%newline%newline" />
        </layout>
      </appender>

      <appender name="WcfService.Api_Error" type="log4net.Appender.RollingFileAppender" LEVEL="ERROR">
        <file value="./Logs\API\logError_" />
        <appendToFile value="true" />
        <datePattern value="yyyyMMdd'.txt'" />
        <rollingStyle value="Date" />
        <staticLogFileName value="false" />
        <layout type="log4net.Layout.PatternLayout">
          <header value="[Header]&#13;&#10;" />
          <footer value="[Footer]&#13;&#10;" />
          <conversionPattern value="%date{dd/MM/yyyy-HH:mm:ss} %m%newline%exception" />
        </layout>
        <filter type="log4net.Filter.LevelRangeFilter">
          <param name="LevelMin" value="ERROR" />
          <param name="LevelMax" value="ERROR" />
        </filter>
      </appender>

      <appender name="WcfService.Api_Info" type="log4net.Appender.RollingFileAppender" LEVEL="INFO">
        <file value="./Logs\API\logInfo_" />
        <appendToFile value="true" />
        <datePattern value="yyyyMMdd'.txt'" />
        <rollingStyle value="Date" />
        <staticLogFileName value="false" />
        <layout type="log4net.Layout.PatternLayout">
          <header value="[Header]&#13;&#10;" />
          <footer value="[Footer]&#13;&#10;" />
          <conversionPattern value="%date{dd/MM/yyyy-HH:mm:ss} %m%newline%exception" />
        </layout>
        <filter type="log4net.Filter.LevelRangeFilter">
          <param name="LevelMin" value="INFO" />
          <param name="LevelMax" value="INFO" />
        </filter>
      </appender>
     
      <root>
        <level value="All" />
        <appender-ref ref="RollingLogFileAppender" />
      </root>

      <logger name="WcfService" additivity="false">
        <appender-ref ref="WcfService.Api_Info" />
        <appender-ref ref="WcfService.Api_Error" />
      </logger>
    </log4net>
    </configuration>

     
  • 相关阅读:
    Hadoop命令手册
    编程算法
    综合8种子排序算法总结和比较
    android 创建一个新的每次project什么时候 请问自己主动 参加 V7依赖?
    【JDBC】java PreparedStatement操作oracle数据库
    【cocos2dx 加载资源目录】
    Project Euler:Problem 39 Integer right triangles
    矿Java开发学习之旅------&gt;Java排序算法经典的二分法插入排序
    [React Intl] Render Content with Placeholders using react-intl FormattedMessage
    [React Intl] Install and Configure the Entry Point of react-intl
  • 原文地址:https://www.cnblogs.com/seesea125/p/2741652.html
Copyright © 2011-2022 走看看