WCF基礎教程之異常處理:你的Try..Catch語句真的能捕獲到異常嗎?

雲霏霏發表於2015-01-23

  在上一篇WCF基礎教程之開篇:建立、測試和呼叫WCF部落格中,我們簡單的介紹瞭如何建立一個WCF服務並呼叫這個服務。其實,上一篇部落格主要是為了今天這篇部落格做鋪墊,考慮到網上大多數WCF教程都是從基礎講起的,大家平時工作可能只是去呼叫和修改WCF的一些方法,而並未建立和配置過WCF,如果大家通過網上的教程去一步一步的建立和配置WCF,中途遇到錯誤,特別是WCF的配置這塊很容易出錯,難免會浪費時間。今天,我們就主要來說一下WCF中服務端和客戶端的異常處理。

 

 一、WCF異常處理機制

   接著昨天的例子,我們在UserService中新增一個新的方法,或者直接修改DoWork方法,丟擲一個異常,程式碼如下:

    [OperationContract]
        public void GetMessage()
        {
            throw new Exception("System Error!");
        }

下面,我們在客戶端呼叫這個方法,程式碼如下:

  public void GetData()
        {
            UserServiceReference.UserServiceClient client = new UserServiceReference.UserServiceClient();
            client.GetMessageCompleted += client_GetMessageCompleted;
            client.GetMessageAsync();
        }

        void client_GetMessageCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
             if(e.Error != null)
            {
                MessageBox.Show(e.Error.Message);
            }
        }

這裡值得注意的是,在非同步方法執行完成後,引數e會攜帶WCF丟擲的異常資訊,儲存在e.Error中。下面我們按下F5,來執行看看會發生什麼,如圖:

首先我們看到的是VS在WCF服務端捕獲到了異常,因為我們用的是Debug模式,這裡可以看到詳細的異常資訊,“System Error”,按F5繼續走,會看到如下圖:

到這裡,我們看到已經到了客戶端,已經看不到異常詳細了,繼續按F5,向後走,看到彈出如下訊息框,如圖:

看到這個異常資訊,完全不知道WCF哪裡出現錯了,對於修復Bug的程式設計師來說無疑是一個噩夢。

從上面的例項演示中,我們可以獲知WCF在預設情況下的異常處理行為:對於服務端丟擲的異常(這裡主要指應用異常),客戶端捕獲到的總一個具有相同異常訊息。由於異常型別和訊息固定不變,對於服務的客戶端來說,直接通過捕獲到的異常相關的資訊是無法確定服務端在執行服務操作的時候遇到的具體的錯誤是什麼。

根據微軟MSDN的文件:https://msdn.microsoft.com/zh-cn/library/dd470096(VS.95).aspx,在web專案中新增SilverlightFaultBehavior類,其程式碼如下:

public class SilverlightFaultBehavior : Attribute, IServiceBehavior
   {
      private class SilverlightFaultEndpointBehavior : IEndpointBehavior
      {
         public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
         {
         }

         public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
         {
         }

         public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
         {
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new SilverlightFaultMessageInspector());
         }

         public void Validate(ServiceEndpoint endpoint)
         {
         }

         private class SilverlightFaultMessageInspector : IDispatchMessageInspector
         {
            public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
            {
               return null;
            }

            public void BeforeSendReply(ref Message reply, object correlationState)
            {
               if ((reply != null) && reply.IsFault)
               {
                  HttpResponseMessageProperty property = new HttpResponseMessageProperty();
                  property.StatusCode = HttpStatusCode.OK;
                  reply.Properties[HttpResponseMessageProperty.Name] = property;
               }
            }
         }
      }

      public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
      {
      }

      public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
      {
         foreach (ServiceEndpoint endpoint in serviceDescription.Endpoints)
         {
            endpoint.Behaviors.Add(new SilverlightFaultEndpointBehavior());
         }
      }

      public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
      {
      }
   }
View Code

然後,修改UserService的程式碼如下:

然後,我們在UserService上面,點選滑鼠右鍵,在瀏覽器中預覽一下,然後再客戶端上的UserServiceReference上面,點選右鍵,更新服務引用,然後我們按Ctrl + F5,以release模式執行專案(這樣不會中斷),如圖:

我們看到,這次顯示了不同的錯誤,但是依然沒有丟擲真正的異常資訊,我們還是不知道哪裡出現錯了。不過根據提示訊息,我們可以看到解決辦法,然後開啟webConfig,尋找includeExceptionDetailInFaults,果然有這個配置,如圖:

我們修改includeExceptionDetailInFaults的值為true,然後再執行,我們看到了如下資訊:

哈哈,終於看到真正的異常資訊了。

 下面再來說一種方法,不修改webconfig檔案,如圖:

當然,我們也可以完整的將WCF的異常的拋給客戶端(服務端不做任何錯誤處理),但是這樣可能會洩露一些敏感資訊,並不安全。更多關於WCF異常處理的資訊,可以參考園內大牛的部落格:

WCF技術剖析之二十一: WCF基本的異常處理模式[上篇]

WCF技術剖析之二十一:WCF基本異常處理模式[中篇]

WCF技術剖析之二十一:WCF基本異常處理模式[下篇]

 

 二、客戶端的呼叫WCF和異常處理

   下面,我們修改客戶端程式碼,新增一個代理類,來對WCF的呼叫進行一些封裝,關於WCF中使用回撥函式,可以參考我之前的這篇部落格Silverlight中非同步呼叫WCF服務,傳入回撥函式,程式碼如下:

這裡客戶端的異常e.Error可以通過回撥函式傳遞給頁面,然後做處理。然後,我們修改UserService,如圖:

然後,更新服務引用,我們呼叫這個WCF方法,加上Try...Catch...,大概就變成了下面這個樣子,如圖:

 這時我們按下F5執行,會看到彈出了我們返回的結果:"WCF Result"。

下面我們在顯示結果前加些程式碼,如圖:

然後,F5執行,猜猜會出現什麼情況,按照我們所想的,應該是彈出一個訊息框,對吧,但是,實際情況是這樣的,如圖:

咦,為什麼我們寫的Try...Catch...沒有捕獲到異常呢?程式碼明明在Try...Catch...裡面啊~~,到這裡,我想你們應該清楚我這邊部落格標題的含義了吧~~

如果之前一直是這樣的寫的,以後就要趕緊改啦~~

其實,我們出現異常的這段程式碼,是通過一個Lamda表示式傳進來的一個匿名委託,相當於一個獨立的方法,所以這個方法根本就不在你的Try...Catch...的作用域內。(說的不對,還請指正)。

所以,將Try...Catch...寫到內部就可以了,修改程式碼如下,就可以了,如圖:

 到這裡,就算是說完了。這裡提醒一下大家以後寫程式碼,測試的時候一定要下斷點全部走到,特別是異常處理部分。最後,祝大家工作愉快,歡迎加入QQ交流群,一起學習交流。

 

 作者:雲霏霏

QQ交流群:243633526

 部落格地址:http://www.cnblogs.com/yunfeifei/

 宣告:本部落格原創文字只代表本人工作中在某一時間內總結的觀點或結論,與本人所在單位沒有直接利益關係。非商業,未授權,貼子請以現狀保留,轉載時必須保留此段宣告,且在文章頁面明顯位置給出原文連線。

如果大家感覺我的博文對大家有幫助,請推薦支援一把,給我寫作的動力。

 

相關文章