• WCF中的异常


       一、考虑到安全因素,为了避免将服务端的异常发送给客户端。默认情况下,服务端出现异常会对异常屏蔽处理后,再发送到客户端。所以客户端捕捉到的异常都是同一个FaultException异常。

    例如在服务端直接产生一个空引用异常,客户端捕获到的是上述异常。

    服务端:

     class Program
        {
            static void Main(string[] args)
            {
                ServiceHost host = new ServiceHost(typeof(SayHello));
                host.AddServiceEndpoint(typeof(ISayHello), new WSHttpBinding(), "http://localhost:4216");
                host.Opened += delegate { Console.WriteLine("Service Start!"); };
                host.Open();
                Console.ReadLine();
            }
        }
        [ServiceContract]
        public interface ISayHello
        {
            [OperationContract]
            void Say();
        }
    
        public class SayHello : ISayHello
        {
            public void Say()
            {
                string name = null;
                Console.Write("Hello {0}", name.Length);
            }
        }
    

     客户端:

     class Program
        {
            static void Main(string[] args)
            {
              ISayHello ClientChannel =  ChannelFactory<ISayHello>.CreateChannel(new WSHttpBinding(), new EndpointAddress("http://localhost:4216"));
              try
              {
                  ClientChannel.Say();
              }
              catch (Exception ex)
              {
                  Console.WriteLine(ex.Message);
                  Console.WriteLine(ex.StackTrace);
              }
            }
        }
    

     二、可通过配置ServiceDebugBehavior将IncludeExceptionDetailInFaults,将其置为true。则服务端会将异常原封不动的传递到客户端,该配置默认为false。

     [ServiceBehavior(IncludeExceptionDetailInFaults=true)]
        public class SayHello : ISayHello
        {
            public void Say()
            {
                string name = null;
                Console.Write("Hello {0}", name.Length);
            }
        }
    

     客户端捕捉到空引用异常。

    三、自定义异常信息

    1.直接通过FaultException构造函数,构造异常。

        [ServiceBehavior(IncludeExceptionDetailInFaults=true)]
        public class SayHello : ISayHello
        {
            public void Say()
            {
                throw new FaultException("自定义异常");
            }
        }
    

    2.通过FaultException<TDetail>构造异常。

      TDetail是一个可序列化的数据结构,如下面定义的myException。需要注意的是在操作契约上需要加一个错误契约,   [FaultContract(typeof(myException))]。

       [ServiceContract]
        public interface ISayHello
        {
            [OperationContract]
            [FaultContract(typeof(myException))]
            void Say();
        }
        [ServiceBehavior(IncludeExceptionDetailInFaults=true)]
        public class SayHello : ISayHello
        {
            public void Say()
            {
                myException ex = new myException
                {
                    Message = "自定义异常",
                    OperatorMethodName = "SayHello:Say()"
                };
                throw new FaultException<myException>(ex, ex.Message);
            }
        }
    
        [DataContract]
        public class myException
        {
            [DataMember]
            public string Message;
            [DataMember]
            public string OperatorMethodName;
        }
    

     客户端:

     class Program
        {
            static void Main(string[] args)
            {
                ISayHello ClientChannel = ChannelFactory<ISayHello>.CreateChannel(new WSHttpBinding(), new EndpointAddress("http://localhost:4216"));
              try
              {
                  ClientChannel.Say();
              }
              catch (Exception ex)
              {
                  myException myex = (ex as FaultException<myException>).Detail;
                  Console.WriteLine(myex.Message);
                  Console.WriteLine(myex.OperatorMethodName);
              }
            }
        }
    

     

     使用错误契约需要注意的地方:

      (1)不能再同一个操作方法上声明相同的细节类型。如下所示:

        [ServiceContract]
        public interface ISayHello
        {
            [OperationContract]
            [FaultContract(typeof(myException))]
            [FaultContract(typeof(myException))]
            void Say();
        }
    

     (2)错误契约上的细节类型不能等效。

        [ServiceContract]
        public interface ISayHello
        {
            [OperationContract]
            [FaultContract(typeof(myException),Name="PersonalException",Namespace="www.cnblogs.cn/lh218")]
            [FaultContract(typeof(myError), Name = "PersonalException", Namespace = "www.cnblogs.cn/lh218")]
            void Say();
        }
    

    四、可以指定异常的序列化方式

        默认的情况下异常消息还DataContractSerializer序列化的。可以通过XmlSerializerFormat的SupportFaults,指定使用XmlSerializer序列化异常消息。

       [ServiceContract]
        public interface ISayHello
        {
            [OperationContract]
            [FaultContract(typeof(myException))]
            [XmlSerializerFormat(SupportFaults=true)]
            void Say();
        }
    

     五、错误消息的结构

         由5部分组成

         (1)FaultCode,可以看出是对异常消息的分类。结点内部的Value表示错误类型,SubCode表示错误子代码。

         (2)FaultReason表示错误原因,内部有一个FaultReasonText集合,FaultReasonText支持全球化语言描述。

         (3)FaultNode元素,表示在SOAP消息路由过程中产生异常的结点。

         (4)FaultRole表示处理消息时所担当的角色。

         (5)FaultDetail 即之前描述的错误细节。

     下面代码讲创建一个错误消息:

    using System.Xml;
    using System.Xml.Serialization;
    using System.ServiceModel;
    using System.ServiceModel.Description;
    using System.Runtime.Serialization;
    using System.ServiceModel.Channels;
    namespace ConsoleApplication4
    {
        class Program
        {
            static void Main(string[] args)
            {
                FaultCode subCode = new FaultCode("lh218", "www.cnblogs.com/lh218");
                FaultCode code = new FaultCode("myFault", subCode);
                var text1=new FaultReasonText("异常消息","zh-CN");
                var text2=new FaultReasonText("Exception Message","en-US");
                FaultReason reason=new FaultReason(new FaultReasonText[]{text1,text2});
                myException exDetail = new myException
                {
                    Message = "自定义异常细节",
                    OperatorMethodName = "myException"
                };
                MessageFault fault = MessageFault.CreateFault(code, reason, exDetail, new DataContractSerializer(typeof(myException)), "lhActor","lhNode");
                Message msg = Message.CreateMessage(MessageVersion.Soap12WSAddressing10, fault, "http://myaction");
                Write(msg, @"D://1.txt");
                Console.ReadLine();
            }
    
            public static  void Write(Message msg,string path)
            {
                using (XmlTextWriter writer = new XmlTextWriter(path,Encoding.UTF8))
                {
                    writer.Formatting = Formatting.Indented;
                    msg.WriteMessage(writer);
                }
            }
        }
    }
    

     消息为:

    <s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
      <s:Header>
        <a:Action s:mustUnderstand="1">http://myaction</a:Action>
      </s:Header>
      <s:Body>
        <s:Fault>
          <s:Code>
            <s:Value>s:myFault</s:Value>
            <s:Subcode>
              <s:Value xmlns:a="www.cnblogs.com/lh218">a:lh218</s:Value>
            </s:Subcode>
          </s:Code>
          <s:Reason>
            <s:Text xml:lang="zh-CN">异常消息</s:Text>
            <s:Text xml:lang="en-US">Exception Message</s:Text>
          </s:Reason>
          <s:Node>lhNode</s:Node>
          <s:Role>lhActor</s:Role>
          <s:Detail>
            <PersonalException xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="www.cnblogs.com/lh218">
              <Message>自定义异常细节</Message>
              <OperatorMethodName>myException</OperatorMethodName>
            </PersonalException>
          </s:Detail>
        </s:Fault>
      </s:Body>
    </s:Envelope>
    

     六、异常与消息之间的转换关系

     如上图所示,MessageFault是FaultException和Message直接的中转对象。

     先看Server端:

      FaultException ==》MessageFault转换方法:FaultException对象可以调用CreateMessageFault创建MessageFault对象。

        public virtual MessageFault CreateMessageFault();

     MessageFault   ==》 Message:Message调用CreateMessage创建,传入MessageFault参数。

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

     Client端:

      Message==>MessageFault:MessageFault类型内部的CreateFault方法可以提取异常消息中的MessageFault

         public static MessageFault CreateFault(Message message, int maxBufferSize);

      MessageFault ==》FaultException:FaultException类型提供两个静态方法创建FaultException。

        public static FaultException CreateFault(MessageFault messageFault, params Type[] faultDetailTypes);

        public static FaultException CreateFault(MessageFault messageFault, string action, params Type[] faultDetailTypes);

  • 相关阅读:
    Kafka技术内幕 读书笔记之(三) 消费者:高级API和低级API——消费者拉取数据
    Kafka技术内幕 读书笔记之(三) 消费者:高级API和低级API——消费者再平衡操作
    Kafka技术内幕 读书笔记之(三) 生产者——消费者:高级API和低级API——基础知识
    Kafka技术内幕 读书笔记之(三) 消费者:高级API和低级API——消费者启动和初始化
    Kafka技术内幕 读书笔记之(二) 生产者——服务端网络连接
    Kafka技术内幕 读书笔记之(二) 生产者——新生产者客户端
    Kafka技术内幕 读书笔记之(一) Kafka入门
    Kafka权威指南 读书笔记之(五)深入Kafka
    Kafka权威指南 读书笔记之(四)Kafka 消费者一一从 Kafka读取数据
    java 多文件合并成zip并下载
  • 原文地址:https://www.cnblogs.com/lh218/p/4553250.html
Copyright © 2020-2023  润新知