傳送和接收錯誤

SOAP 錯誤會將錯誤狀況資訊從服務傳送到用戶端,而在雙工案例中,則是以互通的方式從用戶端傳送到服務。 一般來說,服務會定義自訂錯誤內容,並指定透過哪項作業來傳回這些內容 (如需詳細資訊,請參閱定義和指定錯誤 (部分機器翻譯))。本主題將說明在發生錯誤情況時,服務或雙工用戶端如何傳送這些錯誤,以及用戶端或服務應用程式如何處理這些錯誤。 如需 Windows Communication Foundation (WCF) 應用程式中處理錯誤的概觀,請參閱指定及處理合約和服務中的錯誤

傳送 SOAP 錯誤

已宣告的 SOAP 錯誤是其中作業具有指定自訂 SOAP 錯誤類型之 System.ServiceModel.FaultContractAttribute的 SOAP 錯誤。 未宣告的 SOAP 錯誤則是在作業的合約中未指定的 SOAP 錯誤。

傳送已宣告的錯誤

若要傳送已宣告的 SOAP 錯誤,請偵測包含適當 SOAP 錯誤的錯誤情況並擲回新的 System.ServiceModel.FaultException<TDetail>,其中的型別參數為該作業之 FaultContractAttribute 中所指定的新物件型別。 下列程式碼範例將示範如何使用 FaultContractAttribute 來指定 SampleMethod 作業可以傳回 SOAP 錯誤,連同 GreetingFault 的詳細型別。

[OperationContract]
[FaultContractAttribute(
  typeof(GreetingFault),
  Action="http://www.contoso.com/GreetingFault",
  ProtectionLevel=ProtectionLevel.EncryptAndSign
  )]
string SampleMethod(string msg);
<OperationContract, FaultContractAttribute(GetType(GreetingFault), Action:="http://www.contoso.com/GreetingFault", ProtectionLevel:=ProtectionLevel.EncryptAndSign)> _
Function SampleMethod(ByVal msg As String) As String

若要將 GreetingFault 錯誤資訊傳送到用戶端,請捕捉適當的錯誤情況並擲回包含新的 System.ServiceModel.FaultException<TDetail> 物件之新的 GreetingFault (屬於 GreetingFault 型別) 做為引數,如下列程式碼範例所示。 如果用戶端為 WCF 用戶端應用程式,發生這類錯誤時,是受控的例外狀況,其型別為型別 GreetingFaultSystem.ServiceModel.FaultException<TDetail>

throw new FaultException<GreetingFault>(new GreetingFault("A Greeting error occurred. You said: " + msg));
    Throw New FaultException(Of GreetingFault)(New GreetingFault("A Greeting error occurred. You said: " & msg))
End If

傳送未宣告的錯誤

在 WCF 應用程式中傳送未宣告的錯誤可以針對問題協助快速診斷與偵錯,但用於偵錯時功能有限。 在偵錯時,使用 ServiceDebugBehavior.IncludeExceptionDetailInFaults 屬性是較普遍的建議做法。 當您將此值設為 true,用戶端會碰到諸如 FaultException<TDetail> 類別的 ExceptionDetail 例外狀況之類的錯誤。

重要

由於受控的例外狀況會暴露內部應用程式資訊,所以將 ServiceBehaviorAttribute.IncludeExceptionDetailInFaultsServiceDebugBehavior.IncludeExceptionDetailInFaults 設為 true,便可讓 WCF 用戶端取得內部服務作業例外狀況的相關資訊,包括個人識別資訊或其他敏感資訊。

因此,若您只是暫時對服務應用程式進行偵錯,才建議把 ServiceBehaviorAttribute.IncludeExceptionDetailInFaultsServiceDebugBehavior.IncludeExceptionDetailInFaults 設為 true。 此外,若某個方法以這種方式傳回未處理的 Managed 例外狀況,則該方法的 WSDL 不會包含 FaultException<TDetail> 型別之 ExceptionDetail 的合約。 用戶端必須接受有未知 SOAP 錯誤 (以 System.ServiceModel.FaultException 物件的形式傳回 WCF 用戶端) 的可能性,才能正確取得偵錯資訊。

若要傳送未宣告的 SOAP 錯誤,請擲回 System.ServiceModel.FaultException 物件 (亦即,不是泛型 FaultException<TDetail> 型別),並將字串傳送至建構函式。 如果呼叫 FaultException<TDetail>.ToString 方法來提供字串,這種錯誤會以 System.ServiceModel.FaultException 例外狀況的形式擲回,向 WCF 用戶端應用程式公開。

注意

如果您宣告 String 型別的 SOAP 錯誤,則請將此項目做為 FaultException<TDetail> 擲回服務 (當中的型別參數是一個 System.String,而字串值則是指派給 FaultException<TDetail>.Detail 屬性,而且無法透過 FaultException<TDetail>.ToString 取得)。

處理錯誤

在 WCF 用戶端中,通訊期間發生的 SOAP 錯誤如果與用戶端應用程式相關,發生時就會成為受控的例外狀況。 儘管在任何程式的執行期間有許多例外狀況可能發生,但是使用 WCF 用戶端程式設計模型的應用程式有可能處理下列兩種因通訊而產生的例外狀況。

當作業超出指定的逾時期間,就會擲回 TimeoutException 物件。

當服務或用戶端上出現一些可修復的通訊錯誤情況,就會擲回 CommunicationException 物件。

CommunicationException 類別包含兩個重要的衍生型別,分別是 FaultException 和泛型 FaultException<TDetail> 型別。

當接聽項收到未預期的或於作業合約中指定的錯誤,就會擲回 FaultException 例外狀況;當針對應用程式進行偵錯,且服務的 ServiceDebugBehavior.IncludeExceptionDetailInFaults 屬性設為 true 時,就很容易發生這個情況。

收到作業合約中指定的錯誤時,用戶端會擲回 FaultException<TDetail> 例外狀況,以回應雙向作業 (也就是具有 OperationContractAttribute 屬性,且將 IsOneWay 設為 false 的方法)。

注意

當 WCF 服務將 ServiceBehaviorAttribute.IncludeExceptionDetailInFaultsServiceDebugBehavior.IncludeExceptionDetailInFaults 屬性設為 true,用戶端碰到的例外狀況時,為未宣告的 FaultException<TDetail>,屬於 ExceptionDetail 型別。 用戶端可以捕捉此特定錯誤,或是在 FaultException 的 catch 區塊中處理此錯誤。

一般來說,只有 FaultException<TDetail>TimeoutException,和 CommunicationException 例外狀況會與用戶端及服務相關。

注意

而其他例外狀況,當然一定會發生。 未預期的例外狀況包含 System.OutOfMemoryException 之類的災難性失敗;一般來說,應用程式應該不會捕捉到這類方法。

以正確順序來捕捉錯誤例外狀況

由於 FaultException<TDetail> 係衍生自 FaultException,而 FaultException 則是衍生自 CommunicationException,請務必以正確順序來捕捉這些例外狀況。 例如,假如在您第一次捕捉 CommunicationException 時使用 try/catch 區塊,則所有指定與未指定的 SOAP 錯誤都會就地處理;而且一律不會叫用任何後續的 catch 區塊來處理自訂 FaultException<TDetail> 例外狀況。

請記住,一項作業可以傳回的指定錯誤數量不限。 每一項錯誤都具有唯一的型別,而且必須個別處理。

關閉通道時處理例外狀況

在先前的討論中,大部分都是關於處理應用程式訊息期間所傳送的錯誤,亦即,用戶端應用程式對 WCF 用戶端物件呼叫作業時,用戶端所明確傳送的訊息。

就算是本機物件,處理物件也可能引發或遮罩在回收處理序期間所發生的例外狀況。 使用 WCF 用戶端物件時,也會發生類似的情況。 當您呼叫作業時,事實上您是透過已建立的連線來傳送訊息。 如果連線無法完全關閉,或是已經關閉,則關閉通道會擲回例外狀況,就算所有作業都正常傳回也是一樣。

一般來說,用戶端物件通道可透過下列其中一種方式來關閉:

不管什麼情況,關閉通道都會讓通道開始關閉任何基礎通道,進而傳送訊息以支援應用程式層級的複雜功能。 例如,當合約需要工作階段嘗試透過繫結來建立工作階段時 (方法是藉由與服務通道交換訊息,直到建立工作階段為止)。 一旦通道關閉,基礎工作階段通道會通知服務,工作階段已經終止。 在此情況下,如果通道已經中止、關閉,或是因為其他原因而無法使用 (例如,當網路纜線已拔除時),用戶端通道將無法通知服務通道,告知工作階段已終止且可能擲回例外狀況。

必要時中止通道

由於關閉通道也可能會擲回例外狀況,因此我們建議您除了以正確順序來捕捉錯誤例外狀況外,務必記得要中止用來呼叫 catch 區塊的通道。

如果錯誤傳送了與某項作業相關的特定錯誤資訊,而其他作業也可能透過它來傳送資訊時,就不需要中止通道 (儘管這些情況很罕見)。 在其他任何情況中,建議您中止通道。 如需範例參考上述所有端點的示範,請參閱預期的例外狀況

下列程式碼範例將說明如何透過基本用戶端應用程式來處理 SOAP 錯誤例外狀況,包括已宣告與未宣告的錯誤。

注意

此範例程式碼不會使用 using 建構。 由於關閉通道可能會擲回例外狀況,建議您先透過應用程式建立一個 WCF 用戶端,然後在同一個 try 區塊中開啟、使用,並關閉 WCF 用戶端。 如需詳細資訊,請參閱 WCF 用戶端概觀使用「關閉」和「中止」來釋放 WCF 用戶端資源

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using Microsoft.WCF.Documentation;

public class Client
{
  public static void Main()
  {
    // Picks up configuration from the config file.
    SampleServiceClient wcfClient = new SampleServiceClient();
    try
    {
      // Making calls.
      Console.WriteLine("Enter the greeting to send: ");
      string greeting = Console.ReadLine();
      Console.WriteLine("The service responded: " + wcfClient.SampleMethod(greeting));

      Console.WriteLine("Press ENTER to exit:");
      Console.ReadLine();

      // Done with service.
      wcfClient.Close();
      Console.WriteLine("Done!");
    }
    catch (TimeoutException timeProblem)
    {
      Console.WriteLine("The service operation timed out. " + timeProblem.Message);
      Console.ReadLine();
      wcfClient.Abort();
    }
    catch (FaultException<GreetingFault> greetingFault)
    {
      Console.WriteLine(greetingFault.Detail.Message);
      Console.ReadLine();
      wcfClient.Abort();
    }
    catch (FaultException unknownFault)
    {
      Console.WriteLine("An unknown exception was received. " + unknownFault.Message);
      Console.ReadLine();
      wcfClient.Abort();
    }
    catch (CommunicationException commProblem)
    {
      Console.WriteLine("There was a communication problem. " + commProblem.Message + commProblem.StackTrace);
      Console.ReadLine();
      wcfClient.Abort();
    }
  }
}

Imports System.ServiceModel
Imports System.ServiceModel.Channels
Imports Microsoft.WCF.Documentation

Public Class Client
    Public Shared Sub Main()
        ' Picks up configuration from the config file.
        Dim wcfClient As New SampleServiceClient()
        Try
            ' Making calls.
            Console.WriteLine("Enter the greeting to send: ")
            Dim greeting As String = Console.ReadLine()
            Console.WriteLine("The service responded: " & wcfClient.SampleMethod(greeting))

            Console.WriteLine("Press ENTER to exit:")
            Console.ReadLine()

            ' Done with service. 
            wcfClient.Close()
            Console.WriteLine("Done!")
        Catch timeProblem As TimeoutException
            Console.WriteLine("The service operation timed out. " & timeProblem.Message)
            Console.ReadLine()
            wcfClient.Abort()
        Catch greetingFault As FaultException(Of GreetingFault)
            Console.WriteLine(greetingFault.Detail.Message)
            Console.ReadLine()
            wcfClient.Abort()
        Catch unknownFault As FaultException
            Console.WriteLine("An unknown exception was received. " & unknownFault.Message)
            Console.ReadLine()
            wcfClient.Abort()
        Catch commProblem As CommunicationException
            Console.WriteLine("There was a communication problem. " & commProblem.Message + commProblem.StackTrace)
            Console.ReadLine()
            wcfClient.Abort()
        End Try
    End Sub
End Class

另請參閱