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

一線碼農發表於2015-07-24

  

   我們在玩web程式設計的時候,可能你會不經意的見到一些http500的錯誤,我想你應該不會陌生的,原因你應該也知道,伺服器異常嘛,

這時候clr會把這個未處理的異常拋給iis並且包裝成http500的錯誤返回到客戶端就比如下面這樣。

 

 

從這張圖中,我故意輸入了xss字元,然後的然後,web程式自爆異常,其實我想表達的意思就是,雖然說web程式拋異常了,但不代表iis就

掛了,所以iis還是需要給客戶端做出反饋,這就有了http header,和body資訊,同樣的道理,wcf的伺服器異常機制也是這樣。。。service

丟擲了異常,不代表console就掛了,console要做的事情就是把這個異常包裝起來丟給呼叫方,而wcf是怎麼包裝的呢???就是用了這篇所

說的FaultException。。。

 

一:FaultException

1. faultexception是幹什麼的?

  剛才我也說了,這個異常就是wcf來包裝遠端錯誤的,具體的類含義就是表示“SOAP錯誤“,如果你夠細心的話,你還會發現到它有個屬性

叫Serializable,有了它,這個叼毛就可以序列化到Soap訊息中,對伐???

 

2. 如果挖出faultexception?

  挖出這個exception的方法有很多,比如我來造一個“除以0”的異常,如下所示:

Service:

 1     public class HomeService : IHomeService
 2     {
 3         public Student Get(string id)
 4         {
 5             //這裡必然會丟擲異常。。。
 6             var result = Convert.ToInt32(id) / Convert.ToInt32("0");
 7 
 8             return new Student() { ID = Convert.ToInt32(id), Name = "hxc", SNS = "001" };
 9         }
10     }

Client:

 1     public class Program1
 2     {
 3         static void Main(string[] args)
 4         {
 5             using (HomeServiceClient client = new HomeServiceClient())
 6             {
 7                 try
 8                 {
 9                     var result = client.Get("1");
10                 }
11                 catch (Exception ex)
12                 {
13 
14                 }
15             }
16         }
17     }

 

看到了沒有,雖然wcf的service已經丟擲異常了,但是還是被clr用Faultexception包裝起來了,正如你看到了s:Fault節點,仔細往下看的話,

你還會看到faultcode,faultstring,detail等等屬性節點,那下面有個問題就來了,我們平時在Client端都習慣這麼寫。

 1             using (HomeServiceClient client = new HomeServiceClient())
 2             {
 3                 try
 4                 {
 5                     var result = client.Get("1");
 6                 }
 7                 catch (Exception ex)
 8                 {
 9                     client.Abort();
10                 }
11             }

但是這麼寫有個什麼問題呢???就是不管客戶端丟擲什麼異常,我們都習慣用基類異常Exception捕獲,但是wcf有一點非常噁心的就是,

它的異常資訊非常的少,第一眼根本看不出個一二三,這是因為所有的異常你都用頂級的exception捕獲,自然你能知道的資訊就非常少,

這也很正常,如果你想要更詳細的資訊,你是不是應該在Client端寫上更具體的異常捕獲類呢???就比如你現在已經知道的FaultException

是因為伺服器的錯誤都是由它處理的。

 

如果現在你按照上圖中所coding的那樣,你是不是對異常資訊可以瞭解的更深,起碼你知道這個異常的丟擲,絕逼是因為通道是正常的,只是

servcie丟擲異常了而已。。。那你可能要問了,我這話的言外之意就是還有其他異常類也會捕獲wcf丟擲的異常,對的,比如說你的通道出現

故障,這時候會丟擲一個“通訊異常(CommunicationException)”。

 

三:如何挖出“通訊異常”

    挖出這個異常,也是很簡單的,現在我們需要使用”會話級別“的binding,比如說nettcpbinding,wshttpbinding,這裡的話,我選擇

後者,因為是這樣的,第一次伺服器拋異常以後,客戶端和伺服器端通訊通道就會關閉,如果你在客戶端不重新new一個client,那麼這時候你

第二次再使用client的話,這個時候就會產生“通道故障“,丟擲CommunicationException,而當你看到CommunicationException的時候,

你可以非常有自信的說,老子的wcf根本就沒有連線到service,而是在client端就被殺死了。。。下面我演示一下。

 

四:自定義FaultException

  現在你應該知道了,只要是Servcie的Exception都會丟擲 FaultException,對吧,而且你用Fiddler觀察的話,也看的出其中的faultcode

和faultstring貌似都不是很詳細,那我就有一個想法了,既然wcf會自己給我包裝個FaultException,那何不我自己就在發生異常的時候自己包

裝一個自定義的FaultException,然後我可以包裝一些我自己想要告訴客戶端的資訊,這樣的話是不是靈活性非常的大呢???想法很不錯,wcf

也是恩准這麼做的,下面我把service的get方法更改如下,在FaultException中自定義Reason,Code,Action等等自定義資訊。

 1  public class HomeService : IHomeService
 2     {
 3         public Student Get(string id)
 4         {
 5             try
 6             {
 7                 //這裡必然會丟擲異常。。。
 8                 var result = Convert.ToInt32(id) / Convert.ToInt32("0");
 9 
10                 return new Student() { ID = Convert.ToInt32(id), Name = "hxc", SNS = "001" };
11             }
12             catch (Exception ex)
13             {
14                 var reason = new FaultReason("你這個戰鬥力只有五的渣渣。。。 這麼簡單的錯誤都出來了,搞個雞巴毛");
15 
16                 var code = new FaultCode("500");
17 
18                 var faultException = new FaultException(reason, code, "是Get這個王八蛋");
19 
20                 throw faultException;
21             }
22         }
23     }

好了,大概就說這麼多了,我的目的也很簡單,在寫wcf的client的時候,儘量做到異常越具體越好,這樣方便我們儘可能快的排查問題,因為

wcf的異常資訊真的太tmd坑爹了!!!減輕痛苦,從小做起~~~

 

相關文章