在上一篇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) { } }
然后,修改UserService的代碼如下:
然后,我們在UserService上面,點擊鼠標右鍵,在瀏覽器中預覽一下,然后再客戶端上的UserServiceReference上面,點擊右鍵,更新服務引用,然后我們按Ctrl + F5,以release模式運行項目(這樣不會中斷),如圖:
我們看到,這次顯示了不同的錯誤,但是依然沒有拋出真正的異常信息,我們還是不知道哪里出現錯了。不過根據提示消息,我們可以看到解決辦法,然后打開webConfig,尋找includeExceptionDetailInFaults,果然有這個配置,如圖:
我們修改includeExceptionDetailInFaults的值為true,然后再執行,我們看到了如下信息:
哈哈,終於看到真正的異常信息了。
下面再來說一種方法,不修改webconfig文件,如圖:
當然,我們也可以完整的將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/
聲明:本博客原創文字只代表本人工作中在某一時間內總結的觀點或結論,與本人所在單位沒有直接利益關系。非商業,未授權,貼子請以現狀保留,轉載時必須保留此段聲明,且在文章頁面明顯位置給出原文連接。
如果大家感覺我的博文對大家有幫助,請推薦支持一把,給我寫作的動力。