處理WCF異常的方式

來源:互聯網
上載者:User

   任何程式都離不開對異常的處理,良好的異常處理方式可加快尋找出異常的根源,同時也需要避免暴露敏感資訊到異常中。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(20, 0));
                }
                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 { get; set; }
6         [DataMember]
7         public string  ThreadName { get; set; }
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(20, 0));
 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")]標記即可。

(圖片後續補上)

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.