SOAP エラーは、サービスからクライアントにエラー状態情報を伝達し、双方向の場合はクライアントからサービスに相互運用可能な方法で伝えます。 通常、サービスはカスタム 障害コンテンツを定義し、それらを返すことができる操作を指定します。 (詳細については、「 障害の定義と指定」を参照してください)。このトピックでは、対応するエラー状態が発生したときにサービスまたは双方向クライアントがこれらのエラーを送信する方法と、クライアントまたはサービス アプリケーションがこれらのエラーを処理する方法について説明します。 Windows Communication Foundation (WCF) アプリケーションでのエラー処理の概要については、「 コントラクトとサービスでのエラーの指定と処理」を参照してください。
SOAP エラーの送信
宣言された SOAP エラーは、操作にカスタム SOAP エラーの種類を指定する System.ServiceModel.FaultContractAttribute があるエラーです。 宣言されていない SOAP エラーは、操作のコントラクトで指定されていないエラーです。
宣言された障害の送信
宣言された SOAP エラーを送信するには、SOAP エラーが適切なエラー状態を検出し、その操作のFaultContractAttributeで指定された型の新しいオブジェクトである新しいSystem.ServiceModel.FaultException<TDetail>をスローします。 次のコード例は、 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
エラー情報をクライアントに伝達するには、次のコード例のように、適切なエラー状態をキャッチし、新しいGreetingFault
オブジェクトを引数としてGreetingFault
型の新しいSystem.ServiceModel.FaultException<TDetail>をスローします。 クライアントが WCF クライアント アプリケーションの場合、型がGreetingFault
型のSystem.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 に設定すると、ExceptionDetail型の例外などのエラーFaultException<TDetail>発生します。
Von Bedeutung
マネージド例外は内部アプリケーション情報を公開できるため、 ServiceBehaviorAttribute.IncludeExceptionDetailInFaults または ServiceDebugBehavior.IncludeExceptionDetailInFaults を true
に設定すると、WCF クライアントは、個人を特定できるその他の機密情報など、内部サービス操作の例外に関する情報を取得できます。
そのため、 ServiceBehaviorAttribute.IncludeExceptionDetailInFaults または ServiceDebugBehavior.IncludeExceptionDetailInFaults を true
に設定することは、サービス アプリケーションを一時的にデバッグする方法としてのみ推奨されます。 さらに、この方法でハンドルされないマネージド例外を返すメソッドの WSDL には、ExceptionDetail型のFaultException<TDetail>のコントラクトは含まれません。 クライアントは、デバッグ情報を正しく取得するために、不明な SOAP エラー ( System.ServiceModel.FaultException オブジェクトとして WCF クライアントに返される) の可能性を想定する必要があります。
宣言されていない SOAP エラーを送信するには、 System.ServiceModel.FaultException オブジェクト (つまり、ジェネリック型 FaultException<TDetail>ではなく) をスローし、文字列をコンストラクターに渡します。 これは、FaultException<TDetail>.ToString メソッドを呼び出すことによって文字列を使用できる例外System.ServiceModel.FaultExceptionスローされたとして WCF クライアント アプリケーションに公開されます。
注
文字列型の SOAP エラーを宣言し、これをサービスでFaultException<TDetail>としてスローした場合、型パラメーターは文字列値が FaultException<TDetail>.Detail プロパティに割り当てられ、FaultException<TDetail>.ToStringから使用できないSystem.Stringです。
障害の処理
WCF クライアントでは、クライアント アプリケーションに関係する通信中に発生する SOAP エラーは、マネージド例外として発生します。 プログラムの実行中に発生する可能性のある例外は多数ありますが、WCF クライアント プログラミング モデルを使用するアプリケーションでは、通信の結果として次の 2 つの型の例外を処理することが期待できます。
TimeoutException オブジェクトは、操作が指定されたタイムアウト期間を超えるとスローされます。
CommunicationException オブジェクトは、サービスまたはクライアントに回復可能な通信エラー状態がある場合にスローされます。
CommunicationException クラスには、FaultExceptionとジェネリック FaultException<TDetail>型の 2 つの重要な派生型があります。
FaultException 例外は、リスナーが予期しないエラーまたは操作コントラクトで指定されたエラーを受け取ったときにスローされます。通常、これは、アプリケーションがデバッグ中で、サービスに ServiceDebugBehavior.IncludeExceptionDetailInFaults プロパティが true
に設定されている場合に発生します。
FaultException<TDetail>操作コントラクトで指定されたエラーが双方向操作 (つまり、IsOneWayfalse
に設定されたOperationContractAttribute属性を持つメソッド) に応答して受信されると、クライアントで例外がスローされます。
注
WCF サービスに ServiceBehaviorAttribute.IncludeExceptionDetailInFaults プロパティまたは ServiceDebugBehavior.IncludeExceptionDetailInFaults プロパティが設定 true
場合、クライアントはこれを宣言されていない型の FaultException<TDetail> として ExceptionDetail。 クライアントは、この特定の障害をキャッチするか、 FaultExceptionの catch ブロックでエラーを処理できます。
通常、クライアントとサービスには、 FaultException<TDetail>、 TimeoutException、 CommunicationException の例外のみが対象です。
注
もちろん、他の例外も発生します。 予期しない例外には、 System.OutOfMemoryExceptionなどの致命的なエラーが含まれます。通常、アプリケーションではこのようなメソッドをキャッチしないでください。
エラー例外を正しい順序でキャッチする
FaultException<TDetail>はFaultExceptionから派生し、FaultExceptionはCommunicationExceptionから派生するため、これらの例外を適切な順序でキャッチすることが重要です。 たとえば、最初に CommunicationExceptionをキャッチする try/catch ブロックがある場合、指定されたすべての SOAP エラーと指定されていない SOAP エラーがそこで処理されます。カスタム FaultException<TDetail> 例外を処理する後続の catch ブロックは呼び出されません。
1 回の操作で、指定したエラーの数をいくつでも返すことができることに注意してください。 各障害は一意の型であり、個別に処理する必要があります。
チャネルを閉じるときに例外を処理する
前述の説明のほとんどは、アプリケーション メッセージの処理中に送信されたエラー、つまり、クライアント アプリケーションが WCF クライアント オブジェクトに対する操作を呼び出すときにクライアントによって明示的に送信されるメッセージと関係があります。
ローカル オブジェクトが破棄されている場合でも、オブジェクトはリサイクル プロセス中に発生する例外を発生またはマスクできます。 WCF クライアント オブジェクトを使用すると、同様のことが発生する可能性があります。 操作を呼び出すときは、確立された接続経由でメッセージを送信します。 チャネルを閉じると、すべての操作が正常に返された場合でも、接続を正常に閉じることができないか、既に閉じている場合に例外がスローされる可能性があります。
通常、クライアント オブジェクト チャネルは次のいずれかの方法で閉じられます。
WCF クライアント オブジェクトがリサイクルされるとき。
クライアント アプリケーションが ClientBase<TChannel>.Closeを呼び出すとき。
クライアント アプリケーションが ICommunicationObject.Closeを呼び出すとき。
クライアント アプリケーションが、セッションの終了操作である操作を呼び出す場合。
いずれの場合も、チャネルを閉じると、アプリケーション レベルで複雑な機能をサポートするためにメッセージを送信している可能性がある基になるチャネルをチャネルが閉じ始めるよう指示されます。 たとえば、コントラクトでセッションが必要な場合、バインディングはセッションが確立されるまでサービス チャネルとメッセージを交換してセッションを確立しようとします。 チャネルが閉じられると、基になるセッション チャネルは、セッションが終了したことをサービスに通知します。 この場合、チャネルが既に中止、閉じている、または使用できない場合 (ネットワーク ケーブルが取り外されている場合など)、クライアント チャネルはセッションが終了したことをサービス チャネルに通知できず、例外が発生する可能性があります。
必要に応じてチャネルを中止する
チャネルを閉じると例外がスローされる可能性があるため、エラー例外を正しい順序でキャッチするだけでなく、catch ブロックで呼び出しを行う際に使用されたチャネルを中止することが重要です。
エラーが操作に固有のエラー情報を伝達し、他のユーザーが操作を使用できる可能性がある場合は、チャネルを中止する必要はありません (このようなケースはまれです)。 それ以外の場合は、チャネルを中止することをお勧めします。 これらのすべての点を示すサンプルについては、「 予期される例外」を参照してください。
次のコード例は、宣言されたエラーや宣言されていないエラーなど、基本的なクライアント アプリケーションで SOAP エラー例外を処理する方法を示しています。
注
このサンプル コードでは、 using
コンストラクトは使用しません。 チャネルを閉じると例外がスローされる可能性があるため、アプリケーションで最初に WCF クライアントを作成してから、同じ try ブロックで WCF クライアントを開いて使用し、閉じることをお勧めします。 詳細については、「 WCF クライアントの概要 」および「 Close and Abort を使用して 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