在处理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="------------------------------------------------------------ " />
      <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] " />
      <footer value="[Footer] " />
      <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] " />
      <footer value="[Footer] " />
      <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>

利用Attribute和IErrorHandler处理WCF全局异常的更多相关文章

  1. WCF全局异常处理

    在用wcf做为单纯的服务端的时候,发生错误是常有的事情,特别是在调用其他系统提供的接口的时候,发生的一些错误总是让人摸不着头脑,严重影响了错误的定位.做.net web开发的时候,我们可以在Globa ...

  2. Net Core集成Exceptionless分布式日志功能以及全局异常过滤

    Net Core集成Exceptionless分布式日志功能以及全局异常过滤 相信很多朋友都看过我的上篇关于Exceptionless的简单入门教程[asp.Net Core免费开源分布式异常日志收集 ...

  3. 使用spring利用HandlerExceptionResolver实现全局异常捕获

    最近一直没有时间更新是因为一直在更新自己使用的框架. 之后会慢慢带来对之前使用的spring+mvc+mybatis的优化. 会使用一些新的特性,实现一些新的功能. 我会尽量分离业务,封装好再拿出来. ...

  4. Android全局异常捕获

    PS:本文摘抄自<Android高级进阶>,仅供学习使用 Java API提供了一个全局异常捕获处理器,Android引用在Java层捕获Crash依赖的就是Thread.Uncaught ...

  5. Springboot项目全局异常统一处理

    转自https://blog.csdn.net/hao_kkkkk/article/details/80538955 最近在做项目时需要对异常进行全局统一处理,主要是一些分类入库以及记录日志等,因为项 ...

  6. SpringBoot图文教程15—项目异常怎么办?「跳转404错误页面」「全局异常捕获」

    有天上飞的概念,就要有落地的实现 概念十遍不如代码一遍,朋友,希望你把文中所有的代码案例都敲一遍 先赞后看,养成习惯 SpringBoot 图文教程系列文章目录 SpringBoot图文教程1-Spr ...

  7. 在C#代码中应用Log4Net(四)在Winform和Web中捕获全局异常

    毕竟人不是神,谁写的程序都会有bug,有了bug不可怕,可怕的是出错了,你却不知道错误在哪里.所以我们需要将应用程序中抛出的所有异常都记录起来,不然出了错,找问题就能要了你的命.下面我们主要讨论的是如 ...

  8. MVC 好记星不如烂笔头之 ---> 全局异常捕获以及ACTION捕获

    public class BaseController : Controller { /// <summary> /// Called after the action method is ...

  9. spring设置全局异常处理器

    1.spring设置全局异常,它的原理是向上捕获 spring.xml配置 <!--自定义全局异常处理器--> <bean id="globalExceptionResol ...

随机推荐

  1. 【转】在嵌入式Linux和PC机Linux下使用popen函数时,程序运行结果有差异。

    下面程序演示了在嵌入式Linux和PC机Linux下使用popen函数时,程序的运行结果是有差异的. 两个程序 atest.c 和 btest.c,atest 检查是否有 btest 进程运行,如果没 ...

  2. velocity模板实战

    场景:json配置报文转换遇到的问题:1.json报文转换成map,多节点如何处理?数组如何处理? 2.velocity模板处理数组 3.应用之间rabbitmq通讯map反序列化,数组报错?知识点: ...

  3. Java 将任意数组的任意两个位置的数据进行交换

    package yw.fanxing; /** * 自定义泛型测试 * * 写一个方法,将任意数组的任意两个位置的数据进行交换 * * @author yw-tony * */ public clas ...

  4. allegro中Autosilk top, Silkscreen top 和Assembly top三个什么区别(转)

    allegro中Autosilk top, Silkscreen top 和Assembly top三个什么区别(转) Autosilk top, Silkscreen top 和Assembly t ...

  5. Maven的继承与聚合——多模块开发

    一:Maven多模块项目,适用于一些比较大的项目,通过合理的模块拆分,实现代码的复用,便于维护和管理.尤其是一些开源框架,也是采用多模块的方式,提供插件集成,用户可以根据需要配置指定的模块. 二:继承 ...

  6. TClientDataSet 提交时提示 Field value Required 但是未提示具体哪个字段。

    TClientDataSet 提交时提示 Field value Required 但是未提示具体哪个字段. 这个错误特别麻烦,要使用 midas 控件时,虽然很方便.但是出错了根本找不到原因,特别是 ...

  7. .net 设置Webbowser 版本

    .net 里的Webbowser控件默认情况是用IE7来渲染 可修改注册表试用是最新的版本来渲染: using System; using System.Collections.Generic; us ...

  8. 记一次 java 连接 linux ssh服务 权限验证失败的原因和解决过程

    下面的问题我是通过之前的ssh测试类找出原因的,因为我的测试类跑通了,但是程序跑不通,看了一下源码发现还有一处没有进行解密,所以才会权限验证失败. // 出现权限验证失败的原因就在这里,因为老板要求对 ...

  9. 测试开发:Python+Django实现接口测试工具

    Python+Django接口自动化 引言: 最近被几个公司实习生整自闭了,没有基础,想学自动化又不知道怎么去学,没有方向没有头绪,说白了其实就是学习过程中没有成就感,所以学不下去.出于各种花里胡哨的 ...

  10. python 游戏(猜单词Hangman)

    1.游戏思路和流程图 实现功能:随机一个单词让玩家猜测(后续难度实现修改为成语填空,成语必须要有提示,可修改猜的次数,增加连续猜成语,难度系数随着次数的增加而增加) 游戏流程图 2. 单词库和模块 i ...