您的位置:首页 > 其它

利用Attribute和IErrorHandler处理WCF全局异常

2015-05-14 15:13 435 查看

在处理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>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: