處理WCF異常的方式


   任何程序都離不開對異常的處理,良好的異常處理方式可加快尋找出異常的根源,同時也需要避免暴露敏感信息到異常中。WCF這種典型的服務端和客戶端交互的程序,服務端的異常更需要適當的處理。下面以一個簡單的服務為例,說明WCF中處理異常的方式。

WCF服務定義如下,很明顯方法Divide在divisor為0的時候將會拋出異常

View Code
public  class CalculateService : ICalculateService
    {
         public  int Divide( int dividend,  int divisor)
        {
             return dividend / divisor;
        }

         public  int Add( int a,  int b)
        {
             return a + b;
        }
    }

客戶端調用如下:

View Code
  using ( var client =  new CalculateServiceClient())
            {
                 try
                {
                    Console.WriteLine(client.Divide( 200));
                }
                 catch (FaultException ex)
                {
                    Console.WriteLine(ex.Reason);
                }

              }

首先需要知道的是,WCF的異常信息默認是以FaultException的形式返回到客戶端,FaultException的關鍵屬性Reason是對客戶端反饋的最重要信息之一。以上客戶端代碼調用之后,默認的FaultException返回的Message信息如下:

由於內部錯誤,服務器無法處理該請求。有關該錯誤的詳細信息,請打開服務器上的 IncludeExceptionDetailInFaults (從 ServiceBehaviorAttribute 或從 <serviceDebug> 配置行為)以便將異常信息發送回客戶端,或在打開每個 Microsoft .NET Framework 3.0 SDK 文檔的跟蹤的同時檢查服務器跟蹤日志。

根據異常的提示,意思說如果要在客戶端看到詳細的Exception信息,那么請將ServiceBehavior對應的IncludeExceptionDetailInFaults屬性設置為True,通常在配置中表現為如下設置:

View Code
1 <serviceBehaviors>
2         <behavior>
3           <serviceMetadata httpGetEnabled= " True " httpGetUrl= " http://localhost:8733/CalculateService/ "/>
4           <serviceDebug includeExceptionDetailInFaults= "True "  />
5         </behavior>
6       </serviceBehaviors>

通過以上設置之后,客戶端輸出的內容為“嘗試除以零”,這個提示信息跟原始的異常信息是一致,即返回的FaultException中的Reason包含原始異常的Message的值,但是這樣處理之后服務端所報出的異常信息直接傳到了客戶端,比如一些保密信息也可能輸出到了客戶端,因此對於異常信息必須進行一個封裝。最直接的形式莫過於在服務端就把異常給捕獲了,並重新throw一個FaultException

服務端的代碼改進如下,經過以下改進,那么客戶端得到的信息僅僅是"操作失敗",同時服務端也記錄了異常信息(這時IncludeExceptionDetailInFaults是設置為False的)。

View Code
1  try
2             {
3                  return dividend / divisor;
4             }
5              catch (Exception ex)
6             {
7                 Console.WriteLine(ex.Message);
8                  throw  new FaultException( " 操作失敗 ");
9             }

當然這是FaultException的默認用法,FaultException還支持強類型的異常錯誤信息,返回更加豐富和精確的錯誤提示。假設定義如下通用的一個FaultContract類型,將出錯時的用戶名和線程名字記錄到異常信息中,因為異常信息也是通過SOAP格式傳輸的,因此跟定義其他DataContract的方式一樣。

CommonFaultContract
1     [DataContract]
2      public  class CommonFaultContract
3     {
4         [DataMember]
5          public  string UserName {  getset; }
6         [DataMember]
7          public  string  ThreadName {  getset; }
8     }

那么服務方法的接口需要增加如下標記,如果不這樣標記,那么客戶端得到的異常類型依然是FaultException,而不是強類型的異常信息。

 [FaultContract(typeof(CommonFaultContract))]
 int Divide(int dividend, int divisor)

實現方法中拋出異常的部分代碼改成如下:

異常處理
1  catch (Exception ex)
2             {
3                 Console.WriteLine(ex.Message);
4                  throw  new FaultException<CommonFaultContract>( new CommonFaultContract 
5                 {
6                     UserName = Environment.UserName,
7                     ThreadName = System.Threading.Thread.CurrentThread.Name
8                 },  " 操作失敗 ");
9             }

 這時候重新生成客戶端的代理類,然后更新客戶端的代碼如下,紅色部分即獲取強類型的異常錯誤信息。

View Code
 1  try
 2                 {
 3                     Console.WriteLine(client.Divide( 200));
 4                 }
 5                  catch (FaultException<CommonFaultContract> ex)
 6                 {
 7                      Console.WriteLine(ex.Detail.ThreadName);
 8                      Console.WriteLine(ex.Detail.UserName);
 9                     Console.WriteLine(ex.Reason);
10                 }

當然在具體應用中還需要根據需求,返回不同的信息,構建不同的FaultContract。

  以上服務端捕獲的異常方法,適用於方法比較少的情況,如果有十多個方法,一個個去寫try catch然后做標記等,那么工作量會很大,而且代碼也不利於重用。嘗試尋找像MVC Controller那樣的統一處理Exception的方式,將異常處理都放在基類中,那么只要繼承與這個基類的方法都不需要去寫try catch去捕獲異常。但WCF中似乎沒有這樣的機制,放棄了這種做法。

  最近在研究Enterprise Lib中對WCF的支持時,發現Exception Block中還特地有針對WCF程序異常處理的解決方案,而且滿足以上說道的需求,即可記錄異常,又可對異常信息進行封裝。更重要的時,自動處理運行時的異常信息,不需要挨個方法的去寫Try catch。秉承企業庫的優秀傳統,大部分工作還是通過配置就可以完成了,非常好的解決方案。下面介紹具體的使用步驟。

步驟一:

引用以下dll

Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.dll

Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF.dll

Microsoft.Practices.EnterpriseLibrary.Common.dll

Microsoft.Practices.ObjectBuilder2.dll

步驟2:

在具體的實現類中,增加如下屬性標記,其中WcfException為企業庫中Exception Block中的一個異常處理策略,具體如何配置異常處理策略,請參考企業庫的幫助文檔。

[ExceptionShielding("WcfException")]
public class CalculateService : ICalculateService

那么只要增加了[ExceptionShielding("WcfException")]這個屬性標記之后,所有運行時的異常都將交給策略名為WcfException的異常處理block來處理,在這里就可以執行一些異常記錄以及異常封裝的操作。

步驟3:

將異常信息封裝為FaultException,這個動作也是通過配置來完成。在Exception節點中添加一個Fault Contract Exception Handler。

Fault Contract Exception Handler需要設置以下兩個屬性值

exceptionMessage:所有異常封裝后的錯誤信息

faultContractType:即返回異常的faltContract類型,這個類型必須指定一個,哪怕方法中沒有用到也要,如果方法中有用到,那么客戶端那邊就能得到強類型FaultException,否則就是普通的FaultException。這里指定為之前定義的CommonFaultContract

對於faultContract類型的值,還可以通過PropertyMappings來自定義需要從原始異常信息中映射到faultContract的屬性中,這個屬性可選。

  經過以上步驟配置之后,服務端的程序就具備了自動處理異常的功能。客戶端還是跟往常那樣調用,不過具體是用FaultException捕獲異常還是FaultException<T>去捕獲異常,還得根據定義方法中是否標記了FaultContract。之后若定義了其他服務接口,同樣也僅僅需要在實現類上加上[ExceptionShielding("WcfException")]標記即可。

(圖片后續補上)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM