WCF學習筆記2(異常處理)


1:WCF中的異常處理分析

WCF中的異常繼承層次結構,如下圖:

System.Object

      System.Exception
              System.SystemException
                          System.ServiceModel.CommunicationException

                                     System.ServiceModel.FaultException
                                                   System.ServiceModel.FaultException<TDetail>

                                                               System.ServiceModel.Web.WebFaultException

 

 在整個WCF體系下,數據存在的形態大體可以分為兩種:XML和托管對象(Managed Object)。WCF建立在.NET平台下,利用托管語言(C#和VB.NET)開發人員提供了一個面向對象的編程模型,所以,在WCF體系最頂層的數據形態表現為.NET托管對象。而最終服務調用體現在消息的交換上,消息時基於XML的(除了少部分非XML的消息,比如JSON)。從數據轉化的角度上講,WCF起到了一個將數據從這兩種形態數據進行轉化和適配的作用。

在WCF異常處理體系中,對於異常或者錯誤,在XML的世界里最終通過soap Fault消息體現;而在托管對象的世界中,即使相應的Exception對象。

WCF包括三種常見類型的異常:

1) 通訊異常,這通常是因為鏈路的原因,比如服務沒有啟動,網絡阻塞等。這類異常是CommunicationException或者其派生類
2) 狀態異常,這類異常通常是與上文提到的實例模式相關的,當訪問了一個已經銷毀的服務器對象時便會引發此類型的異常,它們通常是ObjectDisposedException
3) 服務異常,由服務端根據具體的業務邏輯觸發,通常是FaultException 值得注意的是當拋出服務異常的時候,不同的實例模式的處理方式有所不同:

PerSession:這種模式下,拋出異常,服務實例將銷毀,客戶端拋出FaultException,客戶端代理對象無法繼續使用

PerCall:這種模式下,拋出異常,服務實例也將銷毀。客戶端代理對象無法繼續使用

Single:這種模式下,拋出異常,服務實例會照舊運行。客戶端代理無法繼續使用

 

所以:WCF的異常處理框架的核心功能就是實現FaultException異常和Fault消息之間的轉換

可以這樣來簡單地描述WCF異常處理框架的功能實現:WCF服務端將拋出的FaultException異常進行序列化,並根絕消息的SOAP規范(SOAP 1.1或SOAP 1.2)和WS-Addressing規范(WS-Addressing 2004和WS-Addressing 1.0)生成Fault消息。被傳入信道層,經過一系列的信道后,該Fault消息最終借助於傳輸層返回到客戶端;客戶端信道層接收到該Fault消息並經過相應的處理后,被反序列化。反序列化的結果即實現對FaultException的重建,WCF最終將重建的FaultException異常拋出,對於最終的開發者而言,感覺就像服務端拋出的FaultException直接被客戶端捕獲了一樣。在上面的內容中我們說過:WCF並不直接進行FaultException和Fault消息之間的轉換,而是借助於MessageFault這一中間對象

2:錯誤契約

以服務端的一個服務方法(被除數為零):

        public int devide(int a, int b)
        {
            return a / b;
        }

測試WCF異常處理機制

1》如果IncludeExceptionDetailInFaults為關閉狀態,而且服務端對服務異常沒有做任何的處理,那么客戶端只能看到:“

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

Server stack trace:
   在 System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter)
   在 System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
   在 System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
   在 System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
   在 System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

Exception rethrown at [0]:
   在 System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   在 System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   在 Contact.Calc.devide(Int32 a, Int32 b)
   在 Client.Program.Main(String[] args) 位置 D:\WCF_study\Contact\Client\Program.cs:行號 20}

。”;

2》如果IncludeExceptionDetailInFaults為開啟狀態(通常開發狀態才可以開啟),而且服務端對服務異常沒有做任何的處理,那么客戶端可以看到:“System.ServiceModel.FaultException`1[System.ServiceModel.ExceptionDetail]: 嘗試
除以零。 (錯誤詳細信息等於 很可能由 IncludeExceptionDetailInFaults=true 創建的 E
xceptionDetail,其值為:
System.DivideByZeroException: 嘗試除以零。
   在 Service.CalcService.devide(Int32 a, Int32 b) 位置 D:\WCF_study\Contact\Ser
vice\CalcService.cs:行號 18
   在 SyncInvokedevide(Object , Object[] , Object[] )
   在 System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, O
bject[] inputs, Object[]& outputs)
   在 System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(Messag
eRpc& rpc)
   在 System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(Me
ssageRpc& rpc)
   在 System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(M
essageRpc& rpc)
   在 System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(Me
ssageRpc& rpc)
   在 System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(M
essageRpc& rpc)
   在 System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(Me
ssageRpc& rpc)
   在 System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(..
.)。”

3》如果IncludeExceptionDetailInFaults為關閉狀態,而且服務端[FaultContract(typeof(DivideByZeroException))],服務方法針對除數為零拋出了throw new FaultException<DivideByZeroException>(new DivideByZeroException(), "除數為零"),那么客戶端可以看到:“System.ServiceModel.FaultException`1[System.DivideByZeroException]: 除數為零 (錯
誤詳細信息等於 System.DivideByZeroException: 嘗試除以零。)。”

4》如果IncludeExceptionDetailInFaults為關閉狀態,而且服務端去掉[FaultContract(typeof(DivideByZeroException))],服務方法針對除數為零拋出了throw new FaultException<DivideByZeroException>(new DivideByZeroException(), "除數為零"),那么客戶端可以看到:”

System.ServiceModel.FaultException: 除數為零

Server stack trace:
   在 System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRunt
ime operation, ProxyRpc& rpc)
   在 System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean on
eway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan tim
eout)
   在 System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCall
Message methodCall, ProxyOperationRuntime operation)
   在 System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

Exception rethrown at [0]:
   在 System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage req
Msg, IMessage retMsg)
   在 System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgDa
ta, Int32 type)
   在 Contact.Calc.devide(Int32 a, Int32 b)
   在 Client.Program.Main(String[] args) 位置 D:\WCF_study\Contact\Client\Progra
m.cs:行號 20

3:通過錯誤處理擴展來異常提升和記錄日志

WCF允許開發者定制默認的異常報告和異常傳遞,甚至為定制日志提供了一個鈎子.需要實現IErroHandler接口,實現HandleError和ProvideFault方法。

ProvideFault方法:如果出現應用異常,返回客戶端被阻止,立即調用次方法,可以在此方法進行異常提升,不能在此方法中做些耗時的操作

HandleError方法:不會阻止返回客戶端,調用次方法的是后台使用的一個單獨線程,而不是用來處理服務請求的線程,可以在此方法中做一些比如記錄日志到數據庫中和錯誤跟蹤的耗時操作。

什么異常提升:首先它是一種解藕技巧。一般服務可以使用下游對象,這些對象也可以被各種服務調用,所以考慮到系統的松散耦合,服務的下游對象不應該依賴於調用它們的服務的特定錯誤契約,當下游對象出現錯誤時,只需拋出常規的CLR異常。服務要做的就是使用錯誤處理擴展檢查拋出的異常,如果異常屬於faultException<T>中的T類型,同時又屬於錯誤契約操作的一部分,那么服務就能夠將該異常提升為完整的faultException類型。

 

 

 

 

 


免責聲明!

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



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