前面几篇烂文中所介绍到的错误方式,都是在操作协定的实现代码中抛出 FaultException 或者带泛型参数的detail方案,有些时候,错误的处理方法比较相似,可是要每个操作协定去处理,似乎也太麻烦,此时就应当考虑统一处理了。

在 System.ServiceModel.Dispatcher 命名空间下,有一个 IErrorHandler 接口,这个接口就是让我们统一处理错误的。

先来认识一下这个接口。

    public interface IErrorHandler
{ bool HandleError(Exception error); void ProvideFault(Exception error, MessageVersion version, ref Message fault); }

HandleError 方法是先判断一下,这个错误是否应该让会话中止,通常我们直接返回 true,表示会话不会中止,如果返回false,通信会话会被中止,这会使得服务实例被释放(单个实例模式除外,因为单例模式下如果把实例释放,就导致其他客户端无法调用服务了),从而使数据丢失,所以,还是返回true好一点,表示错误由你来处理。

ProvideFault 方法就是用来产生 SOAP 错误消息的,可以在这里对异常进行统一封装。

IErrorHandler 的实现类只能插入到 Channel Dispatcher中,这表明它是跟通道层相关的。最简单的方法就是把实现了 IErrorHandler 接口的类型实例直接通过 ServiceHost 来获取通道调度程序并加入到 ErrorHandlers 集合中。但,为了便于扩展和配置,老周还是建议宁可麻烦一点,扩展一个 behavior 来添加这个 handler。

按照老周一向的风格,理论和原理部分已经说完了,下面就是上菜,哦不,是上示例时间。

照旧,我们得先弄个示例服务。下面代码定义了一个计算加法运算和二次方运算的协定。

    [ServiceContract(Namespace = "sp-test", Name = "demo", ConfigurationName = "testContract")]
public interface ITest
{
[OperationContract]
int Add(int a, int b);
[OperationContract]
int SQR(int n);
}

然后,实现它。

    [ServiceBehavior(ConfigurationName = "sv")]
class TestSvr : ITest
{
public int Add(int a, int b)
{
if (a < ) throw new ArgumentException("值不能小于0", nameof(a));
if (b < ) throw new ArgumentException("值不能小于0", nameof(b));
return a + b;
} public int SQR(int n)
{
if (n == ) throw new Exception("参数不能为0");
return n * n;
}
}

在实现代码中,都对传入参数进行验证,并抛出对应的异常,这个相信大伙能看懂。

接下来才是主角出场。

来,实现一个自定义的 Error Handler。

    public class CustErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
return true;
} public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
// 1、封装异常
string errText = error.Message;
FaultException fex = new FaultException(errText);
// 2、产生 MessageFault
MessageFault msg_fault = fex.CreateMessageFault();
// 3、产生新的消息
fault = Message.CreateMessage(version, msg_fault, "cust-fault");
}
}

用 FaultException 来封装错误信息是关键一步,如果必要可以用 FaultException<TDetail> ,因为这里没有定义错误协定类,故而用 FaultException 就OK了。

随后,通过 FaultException ,可以生成一个 MessageFault 对象,最后就是产生新的 Message ,这条消息是要传回客户端的。此处咱们用的是以下CreateMessage 方法。

public static Message CreateMessage(MessageVersion version, MessageFault fault, string action);

version 参数好办,只直接引用 ProvideFault 方法中传入的值即可,第二个参数就是刚刚产生的 MessageFault 实例,最后还得指定一个 action,这个你可以自己取,反正一个字符串就行。

下面实现 behavior,这里以实现End Point 层面的 behavior 为例。

    public class CustEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
return;
} public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
return; //无需处理
} public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
ChannelDispatcher cndisp = endpointDispatcher.ChannelDispatcher;
// 加入自定义的错误处理程序
CustErrorHandler cehdlr = null;
cehdlr = (CustErrorHandler)cndisp.ErrorHandlers.FirstOrDefault();
if (cehdlr == null)
{
cehdlr = new CustErrorHandler();
}
cndisp.ErrorHandlers.Add(cehdlr);
} public void Validate(ServiceEndpoint endpoint)
{
return;
}
}

ApplyClientBehavior 方法是客户端上面调用的,此处我们不需要,只要实现 ApplyDispatchBehavior 方法即可,它是用在服务器上的。

按理说,到这里就完事了,但是,如果用配置文件来配置的话,还是不够的,当然了,用代码来配置是没问题的。

为了能让这个自定义的behavior能在配置文件中用,需要实现抽象类 BehaviorExtensionElement。

    public class CustBehaviorExtElement : BehaviorExtensionElement
{
public override Type BehaviorType => typeof(CustEndpointBehavior); protected override object CreateBehavior()
{
return new CustEndpointBehavior();
}
}

这个我不解释了,简单。

打开配置文件,我们需要在配置文件中注册这个自定义的元素。

  <system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="custerr" type="TestSvrSample.CustBehaviorExtElement, TestSvrSample"/>
</behaviorExtensions>
</extensions>
</system.serviceModel>

注意,type 属性指定的是我们刚刚实现抽象类 BehaviorExtensionElement 的那个类,不仅要写上完整类型路径,还要写上所在程序集的名字,不然会找不到。name 属性用来指定在配置中使用时,其元素的名称。

比如,可以这样配置。

    <behaviors>
<endpointBehaviors>
<behavior name="bhv">
<custerr />
</behavior>
</endpointBehaviors>
</behaviors>

其中的 custerr 元素就是刚才在 behaviorExtensions 中所指定的 name 属性。

最后可以在终结点配置中引用这个 behavior。

      <service name="sv">
<endpoint address="http://127.0.0.1:3655/test" binding="basicHttpBinding" contract="testContract" behaviorConfiguration="bhv"/>
</service>

好了,大功告成,现在,可以测试一下调用,调用中,我故意传个不合适的参数值。

                    ITest ch = factory.CreateChannel();
try
{
int r = ch.Add(-, );
Console.WriteLine(r);
}
catch(FaultException faultex)
{
FaultReason reason = faultex.Reason;
string text = reason.GetMatchingTranslation().Text;
Console.WriteLine($"错误:{text}");
}

运行后,客户端捕捉到的错误信息如下图所示。

OK,本文到此结束,开饭。

示例代码下载:http://files.cnblogs.com/files/tcjiaan/IErrorHandlerSample.zip

【WCF】错误处理(四):一刀切——IErrorHandler的更多相关文章

  1. .NET 3.5 安装错误的四个原因及解决方法

    .net framework 3.5 安装错误的四个常见原因及解决方法,飓风软件站整理,转载请注明. 1.清除所有版本 .NET Framework  安装错误后在系统中遗留的文件: 如果您以往安装过 ...

  2. WCF初探-20:WCF错误协定

    WCF错误协定概述 在所有托管应用程序中,处理错误由 Exception 对象表示. 在基于 SOAP 的应用程序(如 WCF 应用程序)中,服务方法使用 SOAP 错误消息来传递处理错误信息. SO ...

  3. WCF编程系列(四)配置文件

    WCF编程系列(四)配置文件   .NET应用程序的配置文件 前述示例中Host项目中的App.config以及Client项目中的App.config称为应用程序配置文件,通过该文件配置可控制程序的 ...

  4. WCF错误:由于目标计算机积极拒绝,无法连接;127.0.0.1:3456

    问题描述 最近Windows打完补丁,原来部署在本机的WCF无法连接:出现如下WCF错误:由于目标计算机积极拒绝,无法连接:127.0.0.1:3456 解决方案 检查一下本机的服务:NetTcpAc ...

  5. WCF错误处理

    介绍 WCF(Windows Communication Foundation) -异常处理:一般Exception的处理,FaultException和FaultException<T> ...

  6. 十五天精通WCF——第十四天 一起聊聊FaultException

     我们在玩web编程的时候,可能你会不经意的见到一些http500的错误,我想你应该不会陌生的,原因你应该也知道,服务器异常嘛, 这时候clr会把这个未处理的异常抛给iis并且包装成http500的错 ...

  7. WCF入门(四)---WCF架构

    WCF是一个分层架构,为开发各种分布式应用的充分支持.该体系结构在下面将详细说明. 约定 约定层旁边就是应用层,并含有类似于现实世界的约定,指定服务和什么样的信息可以访问它会使操作的信息.约定基本都是 ...

  8. [转]十五天精通WCF——第十四天 一起聊聊FaultException

     我们在玩web编程的时候,可能你会不经意的见到一些http500的错误,我想你应该不会陌生的,原因你应该也知道,服务器异常嘛, 这时候clr会把这个未处理的异常抛给iis并且包装成http500的错 ...

  9. WCF学习系列四--【WCF Interview Questions – Part 4 翻译系列】

    WCF Interview Questions – Part 4   This WCF service tutorial is part-4 in series of WCF Interview Qu ...

随机推荐

  1. iOS回顾笔记( 01 )

    html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,bi ...

  2. Android 自定义 View 圆形进度条总结

    Android 自定义圆形进度条总结 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 微信公众号:牙锅子 源码:CircleProgress 文中如有纰漏,欢迎大家留言指出. 最近 ...

  3. 考试模块 - ERP数据流

    快速链接:人力资源知识体系索引 本主题主要列出考试中涉及到的所有表. 步骤 操作 相关表 说明 1 考试辅助资料   基础资料,见附表1 2 题库(109130) HXExmGp   3 试题 HXE ...

  4. html 框架 2017-03-11

    1. 框架frameset   (不在body中编写) Eg: 显示如下框架 第一步: <frameset rows="100,100,*" frmameborder=&qu ...

  5. ES3:ElasticSearch 索引

    ElasticSearch是文档型数据库,索引(Index)定义了文档的逻辑存储和字段类型,每个索引可以包含多个文档类型,文档类型是文档的集合,文档以索引定义的逻辑存储模型,比如,指定分片和副本的数量 ...

  6. 非服务器的定期校正时间 Anacron

    与服务器不同,编程和办公用计算机不是连续24小时运行的.开关机的时间不固定,类似较时这样的任务无法保证运行. 对于这类机器,可以考虑使用 Anacron 进行设置. 在 Archlinux 中, An ...

  7. 对bootstrap不同版本的总结

    之前以为bootstrap2和bootstrap3没啥区别,无非就是功能增加了,简直是误区啊 bootstrap3与bootstrap2版本语法都不同啦 栅格写法 col-md-3/span3 文本效 ...

  8. selenium+python 自动化中界面滚动条操作方法

    虽然webdriver提供了操作浏览器的前进和后退的方法,但对于浏览器滚动条并没有提供相应的操作方法,以下使用的方法: 借助JavaScript来控制浏览器的滚动条,webdriver提供了execu ...

  9. 引用类型-----Object

    本文章转载至:https://segmentfault.com/a/1190000003984584 关于引用类型的概念: 引用类型:引用类型的值(对象)是引用类型的一个实例: 对象:是某个特定引用类 ...

  10. (9)集合之Set,HashSet,TreeSet

    TreeSet子类 注意事项: 1.向TreeSet添加元素的时候,如果元素本身具备了自然顺序的特性,那么就按照元素自然顺序的特性进行排序存储 2.往TreeSet添加元素的时候,如果元素本身不具备自 ...