連載
» 2009年08月14日 00時00分 公開

第4回 フィルタ属性による認証/キャッシュ/セキュリティ対策の実装連載:ASP.NET MVC入門(4/5 ページ)

[山田祥寛(http://www.wings.msn.to/),著]

■HandleError属性のプロパティ

 以上がHandleError属性の基本的な挙動であるが、先ほども述べたように、HandleError属性にはさまざまなプロパティが用意されている。

属性 概要
ExceptionType 対応する例外の種類
Master カスタム・エラー・ページに適用するマスター・ページ(ビュー名)
Order フィルタを適用する順番
View 表示すべきカスタム・エラー・ページ
表5 HandleError属性の主なプロパティ

 以下では、これらを使った具体的な例について見ていくことにしよう。

(1)カスタム・エラー・ページを指定する

 <customErrors>要素のdefaultRedirect属性で、指定されたものとは違うビュー・スクリプトを、カスタム・エラー・ページとして呼び出したい場合には、Viewプロパティを指定すればよい。

[HandleError(View="Error2")]
public String Dummy() {

  ……中略……

}

<HandleError(View:="Error2")> _
Function Dummy() As String

  ……中略……

End Function

リスト11 カスタム・エラー・ページのビュー名を明示的に指定(上:ResultController.cs、下:ResultController.vb)

 この場合、例外が発生すると、/Views/Shared/Error2.aspxが呼び出されることになる(もちろん、Error2.aspxはあらかじめ用意しておく必要がある)。

(2)特定の例外情報とカスタム・エラー・ページを関連付ける

 次に、特定の例外に対して、カスタム・エラー・ページを関連付けてみよう。この場合は、ExceptionTypeプロパティを指定する。

[HandleError(View="Error2",
         ExceptionType=typeof(NotImplementedException))]
public String Dummy() {
  throw new NotImplementedException("エラーが発生しました!!");
  return "";
}

<HandleError(View:="Error2", _
         ExceptionType:=GetType(NotImplementedException))> _
Function Dummy() As String
  Throw New NotImplementedException("エラーが発生しました!!")
  Return ""
End Function

リスト12 例外の種類を特定した例(上:ResultController.cs、下:ResultController.vb)

 この場合、NotImplementedException例外が発生した場合にのみ、Error2.aspxが呼び出されることを意味する。

 ちなみに、このようにExceptionType属性を指定したとき、指定された例外とは異なる例外が発生した場合には、カスタム・エラー・ページは正しく呼び出されないので注意すること。

 例えば、以下のようなコードではExceptionType属性の指定と実際に発生する例外が異なるため、(Error2.aspxではなく)デフォルトのError.aspxが表示されるように思われるかもしれない。

[HandleError(View="Error2", ExceptionType=typeof(NotImplementedException))]
public String Dummy() {
  throw new Exception("エラーが発生しました!!");
  return "";
}

<HandleError(View:="Error2", ExceptionType:=GetType(NotImplementedException))> _
Function Dummy() As String
  Throw New Exception("エラーが発生しました!!")
  Return ""
End Function

リスト13 例外の種類を特定した例(上がResultController.cs、下がResultController.vb)

 しかし、実際に動作させてみると分かるように、そうはならない。このコードを実行すると、Error.aspxではなく、最終的な受け皿であるErrorLast.htmが表示されることが確認できるはずだ。これを正しく表示させるためには、以下のように記述する必要がある。

[HandleError(View="Error2", ExceptionType=typeof(NotImplementedException))]
[HandleError(Order=2)]
public String Dummy() {
  throw new Exception("エラーが発生しました!!");
  return "";
}

<HandleError(View:="Error2", ExceptionType:=GetType(NotImplementedException))> _
<HandleError(Order:=2)> _
Function Dummy() As String
  Throw New Exception("エラーが発生しました!!")
  Return ""
End Function

リスト14 例外の種類を特定した例(上:ResultController.cs、下:ResultController.vb)

 これで最初にNotImplementedException例外がチェックされ、合致した場合にはError2.aspxが、それ以外の例外が発生した場合にはデフォルトのError.aspxが実行されるようになる。Order属性については、あらためてこのすぐ後に解説する。

(3)フィルタの適用順序を指定する

 同じ属性が同一のメソッドに適用された場合、デフォルトではフィルタ属性の実行順序は不定である。つまり、記述順にかかわらず、いずれが先に実行されるかは保証されない。しかし、(2)の例のように適用順序が挙動に大きく影響するようなケースがある。

 例えば、(2)のサンプルでNotImplementedException例外に関連付いたHandleError属性よりもデフォルトのHandleError属性が先に適用されてしまった場合、NotImplementedException例外が発生してもError2.aspxが呼び出されない。意図したような関連付けを行うには、最初に特定の例外に対してひも付けられ、デフォルトのひも付けはその後でなければならないのだ。

 そこで(2)の例では、デフォルトのHandleError属性に「Order=2」を指定している。Orderプロパティはフィルタ属性の実行順序を決めるためのプロパティで、値の小さいものから順に実行される(デフォルト値は「-1」)。つまり、(2)の例では、

   NotImplementedException例外に関連付いたHandleError属性

デフォルトのHandleError属性

の順で実行されることが保証されている。

 似たようなケースで、もう1つ、以下のようなパターンも考えてみよう。

[HandleError()]
public class ResultController : Controller {

  ……中略……

  [HandleError(View="Error2",
      ExceptionType=typeof(NotImplementedException))]

  public String Dummy() {
    throw new NotImplementedException("エラーが発生しました!!");
    return "";
  }
}

<HandleError()> _
Public Class ResultController
  Inherits System.Web.Mvc.Controller

  ……中略……

  <HandleError(View:="Error2", _
      ExceptionType:=GetType(NotImplementedException))>
_
  Function Dummy() As String
    Throw New NotImplementedException("エラーが発生しました!!")
    Return ""
  End Function
End Class

リスト15 クラス、メソッド双方にHandleError属性を適用した例(上:ResultController.cs、下:ResultController.vb)

 本稿冒頭で述べたように、フィルタはコントローラ・クラス全体、もしくは、個々のアクション・メソッドに対して適用できる。この場合、Resultコントローラ全体に対してデフォルトのカスタム・エラー・ページ(Error.aspx)を、Result/DummyアクションでNotImplementedException例外が発生した場合にのみ、Error2.aspxを呼び出すことを意図している。

 しかし、試してみれば分かるように、これは正しく動作しない。コントローラ・クラスとメソッドと双方に同一の属性が適用された場合、デフォルトではコントローラ・クラスに適用された方が無条件に優先されてしまうのだ。このため、Result/Dummyメソッドに指定されたHandleError属性が無視されてしまう。結果、Result/DummyメソッドでNotImplementedException例外が発生しているにもかかわらず、カスタム・エラー・ページとしてはError.aspxが表示されるはずだ。

 このような場合にも、次のようにしてOrder属性を指定する必要がある。

[HandleError(Order=2)]
public class ResultController : Controller

<HandleError(Order:=2)> _
Public Class ResultController
  Inherits System.Web.Mvc.Controller

リスト16 Orderプロパティを明示した例(上:ResultController.cs、下:ResultController.vb)

 これによって、コントローラ・クラスのHandleError属性は2番目に適用(判定)される――つまり、Result/DummyアクションのHandleError属性が最初に判定され、意図した動作が行われるようになるわけだ。

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。