ASP.NET 2.0 Web サービスでグローバル例外ハンドラ

ASP.NET Web サービス でも ASP.NET Web アプリケーションと同じように、Global.asax の Application_Error イベントで例外をハンドルできると思っていたらできなかったので回避策をメモ

何故ハンドル出来ないか?

ここによると、

Web サービスの HttpHandler は、Web サービスの実行中に発生した例外を処理し、その例外を Application_Error イベントを呼び出す前に SOAP 違反に変換します。

と、いうことらしい。

例外をハンドルするためには?

SOAP 拡張機能を作成し、グローバル例外ハンドラで Web サービスの例外を処理します。

と、いうことらしい。具体的にはここを参考に、SoapExtension のサブクラスを作成し、Web.config ファイルでSoap 拡張機能を指定すれば良い。

Soap 拡張機能

とりあえずクラスライブラリプロジェクトを作成し、例外を log4net でロギングするクラス LoggingExtension を作成してみた。
肝心なのは ProcessMessage メソッドで、SoapMessage クラスの Stage プロパティが AfterSerialize または BeforeSeialize の時、 Exception プロパティに SoapException が格納されている。その SoapException の InnerException プロパティに、実際に発生した例外が格納されているので、それを処理するようにした。
その他のメソッドは下記の実装でも特に問題ないようだ。

using System;
using System.Web.Services.Protocols;
using log4net;

namespace Extensions {
    public class LoggingExtension : SoapExtension {
        private static readonly ILog logger = LogManager.GetLogger("default");

        public override void ProcessMessage(SoapMessage message) {
            switch (message.Stage) {
                case SoapMessageStage.AfterSerialize:
                    if (message.Exception != null) {
                        logger.Error("",message.Exception.InnerException);
                    }
                    break;
                default:
                    break;
            }
        }

        public override object GetInitializer(Type serviceType) {
            return null;
        }

        public override object GetInitializer(
            LogicalMethodInfo methodInfo,
            SoapExtensionAttribute attribute) {

            return null;
        }

        public override void Initialize(object initializer) {
        }
    }
}

Web.config

Web サービスプロジェクトで上記のクラスライブラリを参照しただけでは不十分で、さらにWeb.config ファイルで以下の設定をする必要がある。
add タグは という書式で書く。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings/>
  <connectionStrings/>
  <system.web>
    <webServices>
      <soapExtensionTypes>
        <add type="Extensions.LoggingExtension, Extensions"/>
      </soapExtensionTypes>
    </webServices>
  </system.web>
</configuration>

log4net でロギングされない場合

原因は良くわからないが、Web.config が単純なら上記の設定でロギングされる。なんだけど、Web.config が複雑になるとログが出力されないことがある。とりあえず Web サービスクラスで明示的に以下のコードを記述するときちんとロギングされるようになった。

log4net.Config.XmlConfigurator.Configure();