【WCF】错误处理(四):一刀切——IErrorHandler

前面几篇烂文中所介绍到的错误方式,都是在操作协定的实现代码中抛出 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 < 0) throw new ArgumentException("值不能小于0", nameof(a));
            if (b < 0) throw new ArgumentException("值不能小于0", nameof(b));
            return a + b;
        }

        public int SQR(int n)
        {
            if (n == 0) 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(-1, 6);
                        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

时间: 2024-08-01 20:39:48

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

WCF初探-20:WCF错误协定

WCF错误协定概述 在所有托管应用程序中,处理错误由 Exception 对象表示. 在基于 SOAP 的应用程序(如 WCF 应用程序)中,服务方法使用 SOAP 错误消息来传递处理错误信息. SOAP 错误是包括在服务操作元数据中的消息类型,因此会创建一个错误协定,客户端可使用该协定来使操作更加可靠或更具交互性. 此外,由于 SOAP 错误在客户端以 XML 格式表示,这是一种任何 SOAP 平台上的客户端都可以使用的具有极好的互操作性的类型系统,可增加 WCF 应用程序的适用范围. 由于

WCF错误:413 Request Entity Too Large

在我们用WCF传输数据的时候,如果启用默认配置,传输的数据量过大,经常会出这个错误. WCF包含服务端与客户端,所以这个错误可能出现在服务端返回数据给客户端,或客户端传数据给服务端时. 1. 服务端返回数据给客户端报错 在客户端配置文件中,主要是配置maxReceivedMessageSize <system.serviceModel> <bindings> <basicHttpBinding> <binding name="ServiceProxyBi

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

 我们在玩web编程的时候,可能你会不经意的见到一些http500的错误,我想你应该不会陌生的,原因你应该也知道,服务器异常嘛, 这时候clr会把这个未处理的异常抛给iis并且包装成http500的错误返回到客户端,就比如下面这样. 从这张图中,我故意输入了xss字符,然后的然后,web程序自爆异常,其实我想表达的意思就是,虽然说web程序抛异常了,但不代表iis就 挂了,所以iis还是需要给客户端做出反馈,这就有了http header,和body信息,同样的道理,wcf的服务器异常机制也是这

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

WCF Interview Questions – Part 4 This WCF service tutorial is part-4 in series of WCF Interview Questions. Before reading this please go through the following articles in this series. 这是WCF问答教程的第四部分,在阅读之前请先去看下面列出来的文章. WCF Service Interview Questions 

调用WCF错误-There was no endpoint listening

问题描述: 今天在调用WCF服务时候出现了下面的错误. 原因: 调用服务的客户端ip设置成了固定ip.(至于固定ip为什么会导致这个错误,没能去研究) 解决方法: 将客户端ip设置成自动获取.

无废话WCF入门教程四[WCF的配置文件]

一.概述 配置也是WCF编程中的主要组成部分.在以往的.net应用程序中,我们会把DBConn和一些动态加载类及变量写在配置文件里.但WCF有所不同.他指定向客户端公开的服务,包括服务的地址.服务用于发送和接收消息的传输和消息编码,以及服务需要的安全类型等.使用配置文件后,我们无需编译即可修改WCF的变化的信息,提高了程序的灵活性. 如果在代码里写了配置,那么配置文件将不起作用. Web程序在Web.config中配置,应用程序中在App.config中配置. 二.服务配置的主要部分 在Conf

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

今天学习WCF时用C#重写测试例子时,发生错误:由于目标计算机积极拒绝,无法连接.找了N久,网上也没有找到实际的解决方法.查看netstat -an发现当自承载宿主运行时,没有侦听配置的端口.开始总以为是配置问题.到最后终于开始怀疑是程序的问题. 最后检查程序发现, [html] view plaincopy using (ServiceHost host = new ServiceHost(typeof(Artech.WcfServices.Services.CalculatorService

【转】WCF入门教程四[WCF的配置文件]

一.概述 配置也是WCF编程中的主要组成部分.在以往的.net应用程序中,我们会把DBConn和一些动态加载类及变量写在配置文件里.但WCF有所不同.他指定向客户端公开的服务,包括服务的地址.服务用于发送和接收消息的传输和消息编码,以及服务需要的安全类型等.使用配置文件后,我们无需编译即可修改WCF的变化的信息,提高了程序的灵活性. 如果在代码里写了配置,那么配置文件将不起作用. Web程序在Web.config中配置,应用程序中在App.config中配置. 二.服务配置的主要部分 在Conf

WCF错误一例

很久没有做WCF了,这两天弄了个工程,结果发现类作为参数传递时无法传送到服务器端,也不报错.结果试了不少次,还百度了一下,有人说是命名空间的事情,其实命名空间是一样的还是有问题,后来干脆把这些服务器和客户端都要用的类抽出来全部到一个类库,前后台都使用同一个类库.问题得以解决. 使用微软的服务契约应该可以解决,不过这种方式比较麻烦,还是这种两用方式直接一点. 版权声明:本文为博主原创文章,未经博主允许不得转载.