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