第4回 フィルタ属性による認証/キャッシュ/セキュリティ対策の実装:連載:ASP.NET MVC入門(4/5 ページ)
ASP.NET MVCでは、認証やキャッシュ、セキュリティなどの付加機能を、本体のロジックとは別に、属性としてアプリケーションに追加できる。
■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
この場合、例外が発生すると、/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
この場合、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
しかし、実際に動作させてみると分かるように、そうはならない。このコードを実行すると、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
これで最初に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
本稿冒頭で述べたように、フィルタはコントローラ・クラス全体、もしくは、個々のアクション・メソッドに対して適用できる。この場合、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
これによって、コントローラ・クラスのHandleError属性は2番目に適用(判定)される――つまり、Result/DummyアクションのHandleError属性が最初に判定され、意図した動作が行われるようになるわけだ。
Copyright© Digital Advantage Corp. All Rights Reserved.