読者です 読者をやめる 読者になる 読者になる

WCF の例外処理

.net

WCF は例外を握りつぶしてしまう

WCF サービス内で発生した例外は、何もしなければ握り潰されます。そして、代わりに FaultException がクライアントに返されます。

例外の内容も、「例外がハンドルされなかった」という旨に変更されてしまいます。これじゃ何が原因なのか、全く分からないですよね。

クライアントに役立つ情報を返したいなら、自分で FaultException をスローしよう

クライアント側に役立つ情報を返したい場合は、一度例外をキャッチし、自分で FaultException に置き変える必要があります。

public class FooService : IFooService
{
    public void Foo()
    {
        try
        {
            // アプリケーションロジックで例外発生
            throw new ArgumentException("エラー発生");
        }
        catch (ArgumentException ex)
        {
            // 例外をキャッチし、FaultException に置き変える
            throw new FaultException("エラーの説明");
        }
    }
}

ジェネリックな FaultException クラスも使える

ジェネリック版である FaultException クラスも用意されています。こちらを使うと、より詳細な情報をクライアント側に返すことができます。

まずエラー情報を格納するクラスを用意。クライアント〜サービス間で受け渡すので、DataContract を付ける必要があります。

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

このクラスにエラー情報を詰め、FaultException でラップして、再スローします。

public class FooService : IFooService
{
    public void Foo()
    {
        try
        {
            // アプリケーションロジックで例外発生
            throw new ArgumentException("エラー発生");
        }
        catch (ArgumentException ex)
        {
            // 例外をキャッチし、FaultException でラップして
            // 再スローしてみる
            throw new FaultException<ErrorInfo>(
                new ErrorInfo() { Message = "エラーの詳細" },
                new FaultReason("エラーの説明"));
        }
    }
}

FaultException を使う際の注意点として、FaultContract 属性を OperationContract 属性のついたメソッドに付与する必要があります。

[ServiceContract]
public interface IFooService
{
    [OperationContract]
    [FaultContract(typeof(ErrorInfo))]
    void Foo();
}

これで、クライアント側に FaultException が返されます。

例外クラスを FaultException に渡したらダメ

面倒くさがって、Exception 派生クラスをそのまま FaultException で使わないようにして下さい。デバッグならまだしも、リリース時に詳細な例外情報をクライアント側に公開するのは、セキュリティ的にマズイです。

この辺りは、プロジェクトでちゃんと例外処理の方針を決めておかないといけませんね。