WCFの例外処理(クライアントへのエラー通知方法)

WCFでサーバー上で発生した例外は、クライアントへそのままthrowすることはできません。デフォルトではサーバーのスタックフレームの最上位より上に例外が送出されるとクライアントではRemotingExceptionが別途発生し、発生元の情報は通知されません。このため情報が取得できず制御に少々困ります。このため、WCFのエラー通知の仕組みからクライアントへ情報を通知する必要があります。

早速実装方法ですが、WCFの例外のFaultExceptionを派生した例外クラスにFaultExceptionというクラスがあります。このクラスのジェネリックの部分に自作のエラー通知用の自作の型を設定しクライアントへ送ることがでます。

クライアントへ通知する情報を格納するデータコンテナの作成

まずエラー情報をつめる自作の型を作成します。DataContract, DataMember 属性を忘れずつけましょう。

[DataContract]
public class FailureInfo
{
    [DataMember]
    public string Message { get; set; 
}

サーバーが公開する操作のインターフェース

次にサーバー - クライアント間で使用するインターフェースに自作の型が通知されることを宣言するための属性を追加します。

[ServiceContract]
public interface ISservice
{
    [OperationContract]
    [FaultContract(typeof(FailureInfo))] // ここで通知する型を指定する
    void Notice();
}

サーバー側の実装

サーバー側は、簡単のため必ず自作の例外を投げることにします。

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Server : ISservice
{
    // IServiceの実装
    public void Notice()
    {
        var info = new FailureInfo()
        {
            Message = "Error",
        };
        
        throw new FaultException<FailureInfo>(info); // 作成した詳細を設定して例外を投げる
    }
}

クライアント側の実装

クライアントは既にサービスインスタンスを取得済みの状態から、上記で定義した操作を呼び出します。

public void Foo(ISservice service)
{
    try
    {
        service.Notice();
    }
    catch (FaultException<FailureInfo> fex) // ここで補足できる
    {
        // Detail : T なので内容を取り出す
        FailureInfo info = info.Detail;
        Console.WriteLine(info.Message);
        
        // > Error と表示される
    }
}

サーバーで指定した例外が発生するとFaultExceptionが発生してcatch説でエラーを補足することができます。

コメントにもありますが、FaultException.Detaul には ジェネリック型のTが設定されているため(この場合FailureInfo)プロパティを参照してサーバー側で設定された方法を取り出すことができます。

やってはいけないこと

とりあえず、以下のようなコードは書いてはいけません。Tに直接Exceptionとかもやめましょう。せっかく自作したのに意味ないです。

[DataContract]
public class FailureInfo
{
    [DataMember]
    public string StackTrace { get; set; } // 【NG】: StackTraceをそのまま設定する
    
    public Exception Exception { get; set; } // 【NG】: 発生した例外をそのまま設定
}

理由を書くまでも無いと思いますがこれらは、サーバーの内部構造やコンパイルした環境の情報が漏れてしまいます。設定次第でネットワークに内容が丸見えなので注意が必要です。

サーバーからクライアントへのエラー通知

どうしてもサーバー上で発生した例外をクライアントにそのまま投げたい通知したい場合 App.config に以下を記述します。

// https://docs.microsoft.com/ja-jp/dotnet/framework/wcf/samples/service-debug-behavior

<behaviors>
     <serviceBehaviors>
         <behavior name="CalculatorServiceBehavior">
         <!-- WARNING: Setting includeExceptionDetailInFaults = "True" could result in leaking secured server information to the client.-->
         <!-- Please set this to false when deploying -->
             <serviceDebug includeExceptionDetailInFaults="True" httpHelpPageEnabled="True"/>
         </behavior>
     </serviceBehaviors>
</behaviors>

もしくはサービスクラスの[ServiceBehavior]属性にIncludeExceptionDetailInFaults = true を指定します。

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, IncludeExceptionDetailInFaults = true)]
public class Service
{

エラーの扱い方

上述の特徴からWCFのエラーの扱い方は以下方針がよいかと思います。といっても普通のことですが、、、

  • クライアントには操作が失敗した事や理由を柔らかく通知する
  • サーバー側は発生個所(スタックトレース、発生行数)や技術的詳細(引数、判断や進行状況)を細かくロギングする