しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

ASP.NET と IIS の HTTP エラー処理を理解する

以下のような ASP.NET アプリケーションのエラー画面を見たことない人は、ASP.NET 開発者にはいないと思います。この画面を本番環境では出すわけに行かないので普通はカスタマイズしますよね。

f:id:shiba-yan:20140703233722p:plain

しかし、ASP.NET では 404 をアプリケーション的に返しているのに、IIS のデフォルト 404 ページが表示されてしまい、悩まされた経験がある人も多いかと思います。これは ASP.NET と IIS で扱いが異なっているのが原因で、設定も別々に存在しているのでまとめておきたいと思います。

ASP.NET

ASP.NET では Web.config の system.web 要素内に customError 要素を追加して、ステータスコード別にリダイレクト先の URL を設定できるようになります。

customErrors 要素 (ASP.NET 設定スキーマ)

例えば 404 ページをカスタマイズしたい場合には、以下のような定義を追加します。

<system.web>
  <customErrors mode="On">
    <error statusCode="404" redirect="/home/error"/>
  </customErrors>
</system.web>

これで 404 エラーの場合には /home/error にリダイレクトされますが、この customError 要素での設定が有効になるのは ASP.NET のパイプライン内部だけです。

f:id:shiba-yan:20140703233922p:plain

ASP.NET MVC などルーティングを使っている場合には、静的ファイルを除く全てのアクセスが ASP.NET のパイプラインで処理されるので問題ないですが、ASP.NET 内で 404 を返した場合には意図したとおりに動作しなくなります。

例えば、以下のようにアクション内で HttpNotFound メソッドを使った場合ですね。

public ActionResult NotFound()
{
    return HttpNotFound();
}

この場合は ASP.NET パイプラインの最後で 404 にセットされるので、customError 要素での設定が無効になってしまいます。なので、IIS 側の設定も同時に行っておく必要があります。

IIS

当然ですが IIS はルーティングが使われていない、静的なコンテンツを扱う場合にリクエストされたファイルが存在しなければ 404 を返します。この挙動をカスタマイズするための機能がもちろん用意されています。

それが IIS 7 から追加された system.webServer 要素に定義できる httpErrors 要素です。

HTTP Errors <httpErrors> : The Official Microsoft IIS Site

errorMode を Custom にすることで自由に HTTP ステータスコード毎の挙動をカスタマイズできます。例えば、先ほどの ASP.NET と同じように 404 を返した時に /home/error へリダイレクトさせる設定は以下のようになります。

<system.webServer>
  <httpErrors errorMode="Custom">
    <remove statusCode="404" />
    <error statusCode="404" path="/home/error" responseMode="Redirect" />
  </httpErrors>
</system.webServer>

error 要素を追加する前に remove 要素を使って、デフォルトの設定を一応削除しておきます。設定自体は殆ど同じですが、responseMode を ExecuteURL にすることで現在の URL から移動せずにエラーページを表示することも出来ます。

ASP.NET アプリケーションで HTTP エラー画面をカスタマイズする場合には、この 2 つの設定を同時にしておくのが安全ですね。

おまけ:TaaS メソッド

実は IIS の httpErrors を知ったきっかけは、田口さんの ARR を使って 3 秒ぐらいでレスポンスを返すというテクニックでした。

Azure で ARR を使って3秒程度でレスポンスを返す | たんたか

この方法を使うと、リバースプロキシ側がタイムアウトした時に 502 が返ってしまうのですが、httpErrors を使って 502 に対応する設定を用意しておけば、どんな時でも 200 を返すことが出来るらしいです。

なので、この ARR のタイムアウトと httpErrors を使う方法を TaaS メソッドと呼んでます。