MS_HttpApplicationModuleHandler - NetDevInfraWGinOSSConsortium/NetDevInfraWiki GitHub Wiki

HttpApplicationGlobal.asax、HttpModule、HttpHandler

抂芁

HttpApplicationGlobal.asax、
HttpModule、HttpHandler等に぀いお。

補足䞉者の関係を先に: 名前が䌌おいお混同されやすいので、
圹割の違いを先に敎理しおおく。

【リク゚ストの流れ】

  ブラりザ
    ↓
  [ HttpApplication ]  
 アプリ党䜓の共通凊理Global.asax
    ↓
  [ HttpModule ]  [ HttpModule ]  [ HttpModule ] 

      ↑ 党リク゚ストが通る「フィルタヌ」。䜕個でも䞊べられる
    ↓
  [ HttpHandler ]  
 実際に応答を䜜る「終点」。1 ぀だけ
      .aspx → PageHandlerFactory、.ashx → 自䜜ハンドラヌ  
    ↓
  [ HttpModule ]埌凊理 
    ↓
  ブラりザ
個数 圹割 察応する ASP.NET Core
HttpApplication 1プヌル アプリ党䜓のむベント Program.cs / ミドルりェア
HttpModule 䜕個でも 暪断的な凊理認蚌、ログ、曞き換え ミドルりェア
HttpHandler 1 ぀だけ 応答の生成 ゚ンドポむントMap〜

HttpModule  ミドルりェア、HttpHandler  ゚ンドポむント
ずいう察応で捉えるず、ASP.NET Coreぞの移行 の
芋通しが良くなる。

HttpApplicationGlobal.asax

  • 共通の凊理を定矩する。

    • メ゜ッド
    • プロパティ
    • むベント
  • Global.asax ファむルで定矩する。

  • ラむフサむクル

HttpModule

芁求を傍受、参加、たたは倉曎できる。

HttpHandler

ISAPI拡匵機胜に類䌌した機胜。

  • 個々の゚ンドポむント芁求を凊理するために䜿甚する。

  • 芁求を凊理するために䜿甚されるハンドラヌは 1 ぀のみ。

  • むンタヌフェむス IHttpHandler を実装する。

  • アプリ内の HTTP URL たたは URL 拡匵機胜のグルヌプを凊理できる。

  • ラむフサむクル

詳现

ラむフサむクル

  • 同時実行数に合わせおむンスタンス化される。

  • リク゚ストレスポンス間は専有するメンバはスレッドセヌフ。

  • むンスタンス化されたオブゞェクトは耇数ナヌザ間で䜿いたわされる。

  • なお、HttpApplicationずHttpModuleは
    アクセスできるむベントが異なる。

    • HttpApplication

      • アプリケヌションレベルのむベント
      • リク゚ストレベルのむベント
    • HttpModule

      • リク゚ストレベルのむベント

補足原文が蚀う「思わぬ萜ずし穎」の䞭身: この 3 行が
本ペヌゞで最も重芁な蚘述である。

【誀解】
  HttpApplication / HttpModule は「リク゚ストごずに new される」
    → むンスタンス倉数に状態を持っおよい

【実際】
  プヌルむンスタンス プヌルから取り出しお䜿い回される
    ・1 リク゚ストの間は 1 スレッドが専有するここは安党
    ・しかし、リク゚ストが終わるずプヌルに戻る
    ・次のリク゚ストで「別のナヌザヌ」に貞し出される
      → 前のナヌザヌの倀が残っおいるず情報挏掩
public class MyModule : IHttpModule
{
    private string _userId;   // ← 危険。䜿い回される

    public void Init(HttpApplication app)
    {
        app.AuthenticateRequest += (s, e) => { _userId = GetUser(); };
        app.EndRequest          += (s, e) => { Log(_userId); };
        //   → 同䞀リク゚スト内なら動くが、
        //     クリアし忘れるず次のリク゚ストに残る
    }
}

リク゚スト単䜍の状態は HttpContext.Items に持぀のが定石である。

HttpContext.Current.Items["UserId"] = userId;   // リク゚スト終了で砎棄される

Init は初回のみ呌ばれる埌述の★印ため、
Init で確保したものは党リク゚ストで共有される点にも泚意する。

ASP.NET Core のミドルりェアでも同じ眠がある
ミドルりェアのむンスタンスはアプリ起動時に 1 ぀だけ䜜られる。

public class MyMiddleware
{
    private readonly RequestDelegate _next;
    // ここにリク゚スト固有の状態を持っおはいけないSingleton 盞圓

    public async Task InvokeAsync(HttpContext ctx, IScopedService svc)
    {   //                                        ↑ Scoped は匕数で受け取る
        await _next(ctx);
    }
}

DI の Captive Dependency ず同根の問題である。

  • リク゚ストの郜床、IHttpHandlerFactory によっお、
    HttpHandlerをむンスタンス化される。
  • <httpHandlers> の定矩に埓っお、拡匵子や GET,POST などに応じお
    適切なHttpHandlerがむンスタンス化される。

補足IsReusable に泚意: IHttpHandler には
IsReusable プロパティがあり、
true にするず HttpModule ず同様に䜿い回される。

public class MyHandler : IHttpHandler
{
    public bool IsReusable => false;   // ← 状態を持぀なら false
    public void ProcessRequest(HttpContext context) { ... }
}

状態を持たないなら trueむンスタンス生成が省ける、
持぀なら false、ずいう刀断になる。
迷ったら false にしおおく方が安党である。

むベント発生順序

★は初回のみ。

党䜓の初期凊理

  • Global.Application_Start ★
  • IHTTPModule
    • Constructor ★
    • IHTTPModule.Init ★

リク゚ストの開始凊理

  • BeginRequest

    • Global.BeginRequest
    • IHTTPModule.BeginRequest
    • Global.Application_BeginRequest
  • Global.DefaultAuthentication

  • AuthenticateRequest

    • Global.AuthenticateRequest
    • IHTTPModule.AuthenticateRequest
    • Global.Application_AuthenticateRequest
  • PostAuthenticateRequest

    • Global.PostAuthenticateRequest
    • IHTTPModule.PostAuthenticateRequest
  • AuthorizeRequest

    • Global.AuthorizeRequest
    • IHTTPModule.AuthorizeRequest
  • PostAuthorizeRequest

    • Global.PostAuthorizeRequest
    • IHTTPModule.PostAuthorizeRequest
  • ResolveRequestCache

    • Global.ResolveRequestCache
    • IHTTPModule.ResolveRequestCache
  • PostResolveRequestCache

    • Global.PostResolveRequestCache
    • IHTTPModule.PostResolveRequestCache
  • Page.Constructor

  • PostMapRequestHandler

    • Global.PostMapRequestHandler
    • IHTTPModule.PostMapRequestHandler
  • Global.Session_Start ★

  • AcquireRequestState

    • Global.AcquireRequestState
    • IHTTPModule.AcquireRequestState
  • PostAcquireRequestState

    • Global.PostAcquireRequestState
    • IHTTPModule.PostAcquireRequestState
  • PreRequestHandlerExecute

    • Global.PreRequestHandlerExecute
    • IHTTPModule.PreRequestHandlerExecute

ペヌゞの開始凊理

  • Page.PreInit

  • MasterPage.Constructor

  • Init

    • MasterPage.ContentPlaceHolder
    • MasterPage
    • Page
  • Page

    • InitComplete
    • PreLoad
  • Load

    • Page
    • MasterPage
    • MasterPage.ContentPlaceHolder
  • Page

    • Page.LoadComplete

ペヌゞの終了凊理

  • PreRender

    • Page
    • MasterPage
    • MasterPage.ContentPlaceHolder
  • Page

    • PreRenderComplete
    • SaveStateComplete
  • Unload

    • MasterPage.ContentPlaceHolder
    • MasterPage
    • Page

補足ペヌゞ内の順序に぀いお: この 2 節は
ASP.NET Web Formsのむベント発生順 の内容ず察応する。
PageHandlerFactory が生成した Page HttpHandler の䞀皮の
内郚で起きおいるこず
が、ここに展開されおいる。

マスタヌ ペヌゞずの順序が逆転しおいる点が実務䞊の泚意点。

Init   : ContentPlaceHolder → MasterPage → Page   内偎から倖偎ぞ
Load   : Page → MasterPage → ContentPlaceHolder   倖偎から内偎ぞ
Unload : ContentPlaceHolder → MasterPage → Page   内偎から倖偎ぞ

Init の時点ではただ Page の初期化が終わっおいないため、
マスタヌ ペヌゞ偎から Page のプロパティを参照するず
未蚭定である、ずいう事故が起きる。

リク゚ストの終了凊理

  • PostRequestHandlerExecute

    • Global.PostRequestHandlerExecute
    • IHTTPModule.PostRequestHandlerExecute
  • ReleaseRequestState

    • Global.ReleaseRequestState
    • IHTTPModule.ReleaseRequestState
  • PostReleaseRequestState

    • Global.PostReleaseRequestState
    • IHTTPModule.PostReleaseRequestState
  • UpdateRequestCache

    • Global.UpdateRequestCache
    • IHTTPModule.UpdateRequestCache
  • PostUpdateRequestCache

    • Global.PostUpdateRequestCache
    • IHTTPModule.PostUpdateRequestCache
  • EndRequest

    • Global.EndRequest
    • IHTTPModule.EndRequest
  • PreSendRequestHeaders

    • Global.PreSendRequestHeaders
    • IHTTPModule.PreSendRequestHeaders
  • PreSendRequestContent

    • Global.PreSendRequestContent
    • IHTTPModule.PreSendRequestContent

党䜓の終了凊理

  • Global.Session_End ★
  • IHTTPModule.Dispose ★
  • Global.Application_End ★

補足Session_End は圓おにできない: Session_End は
InProc モヌドのずきしか発火しない。

セッション モヌド Session_End
InProc 発火するただし保蚌はされない
StateServer 発火しない
SQLServer 発火しない
Custom 発火しない

さらに InProc でも、

  • アプリケヌション プヌルのリサむクルでは発火しない
    ASP.NET config の periodicRestart
  • プロセスが萜ちれば圓然発火しない

「ログアりト時の埌凊理」を Session_End に曞くのは危険であり、
明瀺的なログアりト凊理か、バッチによる期限切れ凊理で担保する。

䞻芁なむベントの説明

# むベント 説明
1 BeginRequest リク゚ストを受信しお最初に発生
2 AuthenticateRequest ASP.NET がナヌザの認蚌凊理の準備が完了したずきに発生
3 PostAuthenticateRequest Post
4 AuthorizeRequest 暩限の承認凊理の準備が完了したずきに発生
5 PostAuthorizeRequest Post
6 ResolveRequestCache リク゚ストに察しおキャッシュからレスポンスを生成するのか
レスポンスをから生成するのかを決定する
7 PostResolveRequestCache Post
8 PostMapRequestHandler 珟圚の芁求を適切なむベント ハンドラにマップするず発生
9 AcquireRequestState セッション倉数の準備凊理
10 PostAcquireRequestState Post
11 PreRequestHandlerExecute 各ハンドラ実行盎前に発生
12 PostRequestHandlerExecute 各ハンドラ実行盎埌に発生
13 ReleaseRequestState セッション倉数などの倀を曎新・保存する
14 PostReleaseRequestState Post
15 UpdateRequestCache リク゚ストキャッシュの曎新凊理を行う
16 PostUpdateRequestCache Post
17 EndRequest クラむアントのブラりザぞデヌタを送信する盎前に発生
18 PreSendRequestHeaders HTTP ヘッダヌをクラむアントに送信する盎前に発生
19 PreSendRequestContent コンテンツをクラむアントに送信する盎前に発生

移行メモ衚蚘: 原文の「ReleaserequestState」は
ReleaseRequestState の衚蚘揺れず刀断し統䞀した。

補足どのむベントに曞くべきか: 実務でよく䜿うものを敎理しおおく。

やりたいこず むベント 理由
URL の曞き換え BeginRequest ハンドラヌが決たる前
認蚌自前 AuthenticateRequest User を差し替えられる
認可自前 AuthorizeRequest 認蚌枈みの前提で刀定
セッションを䜿う AcquireRequestState 以降 これより前は Session が null
レスポンス本文の加工 BeginRequestResponse.Filter を蚭定
ヘッダヌの远加・削陀 PreSendRequestHeaders 送信盎前
ログ出力 EndRequest 状態が確定しおいる

「BeginRequest で Session を觊ったら null だった」ずいうのが
頻出の぀たずきで、AcquireRequestState より前にセッションは存圚しない

こずが原因である。

リラむト

  • リラむト ≒ URL の曞き換えはHttpHandlerでも可胜のようだが、
  • その他の曞き換え凊理は、芁求パむプラむンに
    モゞュヌルを「プラグむン」できるHttpModuleを䜿うこずが倚い暡様。

URLRequest

URL の曞き換えは、

  • HttpHandlerか、
  • 芁求パむプラむン䞭の Application_BeginRequest ハンドラで
    HttpApplication、HttpModule

行う。

補足珟圚は IIS URL Rewrite が第䞀候補: 自前で
HttpModule を曞くより、IIS の URL Rewrite モゞュヌルを䜿う方が
簡単で高速であるネむティブ モゞュヌルで、マネヌゞド コヌドを通らない。

<system.webServer>
  <rewrite>
    <rules>
      <rule name="Redirect to HTTPS" stopProcessing="true">
        <match url="(.*)" />
        <conditions><add input="{HTTPS}" pattern="off" /></conditions>
        <action type="Redirect" url="https://{HTTP_HOST}/{R:1}"
                redirectType="Permanent" />
      </rule>
    </rules>
  </rewrite>
</system.webServer>

**Rewrite内郚曞き換えず Redirect302/301 を返す**は
別物である点に泚意する。

ASP.NET Core では app.UseRewriter()
Microsoft.AspNetCore.Rewriteが察応する。

app.UseRewriter(new RewriteOptions()
    .AddRedirectToHttps()
    .AddRewrite(@"^old/(.*)", "new/$1", skipRemainingRules: true));

ResponseBody

ResponseBody の曞き換えは、HttpModuleで Response.Filter に
デコレヌトされた ResponseStream を蚭定するこずで行う
HttpApplication、HttpModule。

  • 参考
    • HttpRequestのパラメヌタに小现工をしたい時にどうするか(・ω・) -うさ☆うさ日蚘
      http://d.hatena.ne.jp/machi_pon/20091203/1259842545

補足ASP.NET Core での察応: Response.Filter に盞圓するのは
HttpResponse.Body の差し替えである。

app.Use(async (ctx, next) =>
{
    var original = ctx.Response.Body;
    using var buffer = new MemoryStream();
    ctx.Response.Body = buffer;

    await next();

    buffer.Seek(0, SeekOrigin.Begin);
    var text = await new StreamReader(buffer).ReadToEndAsync();
    text = text.Replace("foo", "bar");          // 加工

    ctx.Response.Body = original;
    await ctx.Response.WriteAsync(text);
});

ただし、本文を䞞ごずバッファリングするため、
メモリ䜿甚量ずストリヌミング性が犠牲になる
。
本圓に必芁かフロント偎や CDN で凊理できないかを
先に怜蚎するのが劥圓である。

ResponseHeader

ResponseHeader の曞き換えは、
芁求パむプラむン䞭の PreSendRequestHeaders ハンドラで行う
HttpApplication、HttpModule。

補足䞍芁なヘッダヌの削陀は web.config でできる: 参考蚘事が
扱っおいる「サヌバヌ情報を隠す」目的なら、
コヌドを曞かずに web.config で察凊できる。

<system.web>
  <httpRuntime enableVersionHeader="false" />   <!-- X-AspNet-Version -->
</system.web>
<system.webServer>
  <httpProtocol>
    <customHeaders>
      <remove name="X-Powered-By" />
      <!-- セキュリティ ヘッダヌの远加もここで -->
      <add name="X-Content-Type-Options" value="nosniff" />
      <add name="Strict-Transport-Security" value="max-age=31536000" />
    </customHeaders>
  </httpProtocol>
  <security>
    <requestFiltering removeServerHeader="true" />  <!-- ServerIIS 10+ -->
  </security>
</system.webServer>

ASP.NET Core では ミドルりェアで远加するのが玠盎である。

app.Use(async (ctx, next) =>
{
    ctx.Response.Headers["X-Content-Type-Options"] = "nosniff";
    await next();
});

PreSendRequestHeaders は ASP.NET Core には無い
そもそも応答の曞き蟌み前に蚭定するのが自然な蚭蚈になっおいる。

参考

microsoft.com

support

Microsoft Learn

違い

HttpModule ず HttpHandler

HttpApplication ず HttpModule

その他

ラむフサむクル

むベント発生順序

リラむト


Tags: 移行, .NET開発, ASP.NET

⚠ **GitHub.com Fallback** ⚠