IIS7.0/IIS7.5 アプリケーションプールのマネージ パイプライン モードの構成

Windows 2008 Server(R2)にASP.NETのアプリケーションをのせたら、ローカル(Windows XP SP3)では動くのに。トップページも開きませんでした。調べたら、アプリケーションプールのマネージパイプラインが「統合モード」なことが原因なもよう。


そもそもIIS7.0以降は6.0以前と大分違うので勉強が必要でした。MSDNの中では、TechNetのこのページが1番分かりやすかったです。IIS5.0、IIS6.0 技術者向け IIS7.0 概要とアーキテクチャ それから、こちらも参考にしました。IIS 7.0 アーキテクチャの概要

            • -

IIS7.0以降は、アプリケーションプールのマネージパイプラインモードに、クラッシックと統合の2つのモードができました。MSDNからの抜粋だとこんな感じです。下記の内容は、上記TechNetのPPTファイル P.18の「IIS7.0におけるASP.NET統合」を見るとスッキリと分かる気がします。

■統合アプリケーション プール モード
アプリケーション プールが統合モードである場合、IISASP.NET の統合要求処理アーキテクチャを利用できます。 アプリケーション プール内のワーカー プロセスが要求を受け取ると、要求は順序付けされたイベントのリストを通じて受け渡しされます。 各イベントでは、必要なネイティブ モジュールおよびマネージ モジュールが呼び出され、要求の一部が処理され、応答が生成されます。

アプリケーション プールを統合モードで実行することには、いくつかのメリットがあります。 まず、IISASP.NET の要求処理モデルが統一されたプロセス モデルとして統合されることです。 このモデルでは、認証など、以前は IISASP.NET で重複していた手順が解消されます。 また、統合モードではあらゆる種類のコンテンツでマネージ機能を利用できるようになります。

■クラシック アプリケーション プール モード
アプリケーション プールがクラシック モードである場合、要求は IIS 6.0 のワーカー プロセス分離モードと同じように処理されます。 ASP.NET の要求は、まず、IIS でネイティブ処理の手順を経た後、マネージ ランタイムでマネージ コードを処理するために Aspnet_isapi.dll にルーティングされます。 最後に、要求は、応答を送信する IIS に再びルーティングされます。

IISASP.NET の要求処理モデルがこのように分離されているために、認証や承認などの一部の処理手順が重複します。 さらに、フォーム認証などのマネージ コード機能は、ASP.NET アプリケーションや、すべての要求が aspnet_isapi.dll によって処理されるようにマッピングするスクリプトがあるアプリケーションでのみ使用できます。

運用環境を IIS 7.0 にアップグレードし、アプリケーションを統合モードのアプリケーション プールに割り当てる前に、統合モードにおける既存のアプリケーションの互換性をテストしてください。 また、アプリケーションをクラシック モードのアプリケーション プールに追加するのは、アプリケーションが統合モードで動作しない場合に限定してください。 たとえば、アプリケーションが IIS からマネージ ランタイムに渡される認証トークンを利用している場合、IIS 7.0 の新しいアーキテクチャにより、このプロセスではアプリケーションに障害が発生します。

ふむふむ、そしてこちらを見てみる。Web アプリケーションの統合モードへの移行

  1. Web.configの設定は、VS2010で作っているので最初からIIS7以降対応になっているのでOK
  2. 統合モードを操作するためのクラスとプロパティも、クラッシックで使えないという該当のプロパティは使ってない

ログを見ると、Global.asaxのApplication_Startあたりで出ているのでさらに検索した結果、MSのIIS作ってる方のサイトの対応でうまくいきました。IIS7 Integrated mode: Request is not available in this context exception in Application_Start

Global.aspx内のApplication_StartメソッドでRequestのHttpContextにアクセスするとExceptionが発生するようです。対応方法としては、該当の処理を別クラスの別メソッドに移植し、Application_BeginRequestメソッド内から呼ぶようにします。移植した別メソッドの内で、staticなprivateなフラグを作っておき、1度処理したら次回のRequest時には処理をしない仕組みを作っておきます。

Global.asax.cs

public class Global : System.Web.HttpApplication
{
    void Application_BeginRequest(object sender, EventArgs e)
    {
        HttpApplication app = (HttpApplication)sender;
        FirstRequestInitialization.Initialize(app.Context);
    }
}

class FirstRequestInitialization
{
    private static bool s_InitializedAlready = false;
    private static Object s_lock = new Object();

    public static void Initialize(HttpContext context)
    {
        if (s_InitializedAlready)
        {
            return;
        }
        lock (s_lock)
        {
            if (s_InitializedAlready)
            {
                return;
            }

            SetAppRoot(context);
            s_InitializedAlready = true;
        }
    }
    
    private static void SetAppRoot(HttpContext context)
    {
        if (context.Application["AppRootName"] == null)
        {
            context.Application["AppRootName"] = context.Request.ApplicationPath;
        }
    }    
}

ほとんど参考にさせて頂いたサイトと一緒ですが、一応UPしておきます。修正前は、SetAppRootはApplication_Startから呼ばれていました。修正後、IIS7.5の環境に発行して無事動作することを確認できました。