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);

 

posted @ 2015-06-04 22:36  lh_cn  阅读(799)  评论(2编辑  收藏  举报