- Apache License, Version 2.0
- Visual Studio 2010
- ASP.NET MVC
- ASP.NET MVC アプリケーション
- 06/07/2011
本連載では、日経 BP 社から発売された「ひと目でわかる ASP.NET MVC アプリケーション開発入門」をもとにして、執筆時に気づいたことや紙面の都合で書ききれなかった技術を紹介します。
- はじめに
- Authorize 属性の動作をみる
- ログイン名によって制御する
- ロールを使う準備をする
- ロール名によって制御する
- 独自のアクション フィルターを作る
- ユーザー情報を SQL Server 上に作成する
- おわりに
今回は、ASP.NET MVC のアクション メソッドを拡張するアクション フィルターについて解説します。アクション フィルターは、メソッドの属性として設定されます。クラスの静的なデータとして設定されるため、クラス内の共通処理を行う場合に使うことができます。
Visual Studio 2010 で ASP.NET MVC アプリケーションを作ると、ログイン機能がテンプレートとして付いてきますが、ログインを制御するのは AccountController クラスで、Authorize という属性が設定されています。この Authorize 属性の中でログインが行われているか否かを判断して、アクション メソッドの動作を制御しています。
アクション フィルターは、Authorize 属性の他に HandleError 属性、OutputCache 属性などがあります。
最初にログイン制御で使われる Authorize 属性の動作をみていきましょう。
Visual Studio 2010 で「ASP.NET MVC 2 Webアプリケーション」を作成します。
まず、HomeController.cs (VB の場合は HomeController.vb)を開いて、次の 2 つのメソッドを追加します。
public ActionResult Normal() { return View(); } [Authorize] public ActionResult LoginUser() { return View(); }
Public Function Normal() As ActionResult return View(); End Function <Authorize> Public Function LoginUser() As ActionResult return View(); End Function
この 2 つのアクション メソッドの違いは、Authorize 属性が付いているか、いないかの違いだけです。
次に、このアクション メソッドで表示するビューを作成します。
1 つは、ログインしないユーザーでも表示できる一般ユーザー用の Normal.aspx というビューです。
もう 1 つは、ログインしたユーザーだけが表示できる LoginUser.aspx というビューです。
ここでは、LoignUser.aspx の例を示します。
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<dynamic>" %> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> LoginUser </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2>LoginUser</h2> <p>これはログインユーザーだけが閲覧できるページです</p> </asp:Content>
<%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> LoginUser </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2>LoginUser</h2> <p>これはログインユーザーだけが閲覧できるページです</p> </asp:Content>
さてコードの準備ができたので、動作を確認してみましょう。あらかじめユーザーを作成しておいて、ログインしていない状態とログインした状態で表示してみましょう。
動作を確認が簡単にできるように Home/Index.aspx に次のようなリンクを作成しておきます。
<ul> <li><%: Html.ActionLink("一般ユーザー用", "Normal")%></li> <li><%: Html.ActionLink("ログインユーザー用","LoginUser") %></li> </ul>
以下が実行した結果です。
動作を確認してみると、ログインしていない状態で、「ログイン ユーザー用」のリンクをクリックすると、ログインするビューにリダイレクトされます。これは、LoginUser アクション メソッドに設定されている Authorize 属性がログイン状態をチェックして、ログインのためのページにジャンプさせているためです。
ログイン制御のジャンプ先は、web.config ファイルに記述されています。
<authentication mode="Forms"> <forms loginUrl="~/Account/LogOn" timeout="2880" /> </authentication>
この loginUrl 属性の値を変えることで、エラー時のジャンプ先のビューを変更することができます。例えば、Home/Error.aspx のビューにジャンプさせる時には次のように loginUrl 属性の値を変更します。
<authentication mode="Forms"> <forms loginUrl="~/Home/Error" timeout="2880" /> </authentication>
ユーザーがログインしているかどうかでチェックができたので、今度はログイン名によってログインの制御をしてみましょう。
まずは、AdminUser アクション メソッドを作成します。
[Authorize(Users="admin")] public ActionResult AdminUser() { return View(); }
<Authorize(Users:="admin")> Public Function AdminUser() As ActionResult Return View() End Function
Authorize 属性の Users パラメーターにログイン名を指定します。
こうすることで、特定のユーザーだけで表示できるページを作成することができます。
次に「admin」という名前のログイン名を作成しておいて、この admin ユーザーだけが表示できる AdminUser.aspx ビューを作ります。
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<dynamic>" %> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> AdminUser </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2>AdminUser</h2> <p>これは管理ユーザーだけが閲覧できるページです</p> </asp:Content>
実行すると、admin ユーザーだけが AdminUser.aspx のページを表示できます。
Authorize 属性では、ロール (役割) を使ったログインの制御も可能です。
ASP.NET MVC アプリケーションで扱う、ユーザー名とロールの関係は次の図になります。
ユーザー名で全てを制御しようとすると、管理ユーザーを追加するたびに属性を書き替えないといけません。これを避けるために、管理ロールを作成して、追加したい管理ユーザーを管理ロールに属するように設定します。
このロールを使うためには、まず Visual Studio 2010 の「プロジェクト」メニューから「ASP.NET 構成」を選択して、ASP.NET Web サイト管理ツールを開きます。
サイト管理ツールはブラウザー上で、ASP.NET アプリケーションのユーザーの管理などができます。
ロールを使うためには、まず「セキュリティ」のリンクをクリックした後で、「ロールの有効化」をクリックします。
「ロールの作成または管理」のリンクをクリックすると、新しいロールが作成できます。
ここでは「administrators」というロールを作成します。
次にユーザー管理のページを開いて、admin ユーザーを administrators ロールに追加します。
これで準備は完了です。ここでは、masuda と admin というユーザーがありますが、admin だけが管理ロールの administrators に属しています。
まずは、ビューを表示するための AdminRole メソッドに、次のように Authorize 属性を追加します。
[Authorize(Roles = "administrators")] public ActionResult AdminRole() { return View(); }
<Authorize(Roles:="administators")> Public Function AdminUser() As ActionResult Return View() End Function
次に、administrators のロールに属しているユーザーだけが表示できるビューを作成していきましょう。AdminRole.aspx を次のように作成します。
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<dynamic>" %> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> AdminUser </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2>AdminUser</h2> <p>これは管理ユーザーだけが閲覧できるページです</p> </asp:Content>
これを実行すると次の図になります。
masuda ユーザーでログインした状態でも管理ロール用のページ AdminUser.aspx を表示することはできません。masuda ユーザーをログアウトして、admin ユーザーでログインし直すと、管理ロールのページを表示することができます。
ここまで既に ASP.NET MVC で用意されている Authorize 属性の解説をしてきましたが、独自のアクション フィルターを作ることもできます。
アクション フィルターは、属性としてアクション メソッドに設定して動作するためのインターフェースが用意されています。
- IActionFilter
アクション メソッドの実行前と実行後にフィルターを実行します。
- IAuthorizationFilter
アクション メソッドの実行前にフィルターを実行します。
- IExceptionFilter
例外がスローされると、フィルターが実行します。
- IResultFilter
アクションの結果の処理前と処理後にフィルターが実行します。
ここでは、IActionFilter インターフェースを使って、アクション メソッドの前後にログを出力できるようにしてみましょう。
public class MyFilterAttribute : FilterAttribute, IActionFilter { public void OnActionExecuting(ActionExecutingContext filterContext) { Debug.Print("メソッド前:{0}", filterContext.ActionDescriptor.ActionName); } public void OnActionExecuted(ActionExecutedContext filterContext) { Debug.Print("メソッド後:{0}", filterContext.ActionDescriptor.ActionName ); } }
Public Class MyFilterAttribute Inherits FilterAttribute Implements IActionFilter Public Sub OnActionExecuting(ByVal filterContext As ActionExecutingContext) _ Implements IActionFilter.OnActionExecuting Debug.Print("メソッド前:{0}", filterContext.ActionDescriptor.ActionName) End Sub Public Sub OnActionExecuted(ByVal filterContext As ActionExecutedContext) _ Implements IActionFilter.OnActionExecuted Debug.Print("メソッド後:{0}", filterContext.ActionDescriptor.ActionName) End Sub End Class
この MyFilterAttribute 属性クラスでは、基本的な動作を FilterAttribute クラスを継承します。実行前と後のフィルター メソッドが実行されるように、IActionFilter インターフェースを継承します。
OnActionExecuting メソッドが実行前に、OnActionExecuted メソッドが実行後になります。
MyFilter 属性は、Authorize 属性などと同じようにアクション メソッドに設定します。
[MyFilter] public ActionResult Item(int id) { return View(); }
<MyFilter()> Public Function Item(ByVal id As Integer) As ActionResult Return View() End Function
Item.aspx ビューを作成して実行すると、Visual Studio 2010 の出力ウィンドウのログ出力が得られます。
IActionFilter インターフェースの OnActionExecuted メソッドでは、アクション メソッドの結果を修正することもできます。たとえば、ViewData プロパティを使い、デバッグ情報をビューに表示することも可能です。
アクション フィルターを次のように、ViewData を使うように書き変えます。
public void OnActionExecuted(ActionExecutedContext filterContext) { Debug.Print("メソッド後:{0}", filterContext.ActionDescriptor.ActionName ); filterContext.Controller.ViewData["debug"] = string.Format("ここでデバッグ情報を表示します id:{0}", filterContext.RequestContext.RouteData.Values["id"] ); }
Public Sub OnActionExecuted(ByVal filterContext As ActionExecutedContext) _ Implements IActionFilter.OnActionExecuted Debug.Print("メソッド後:{0}", filterContext.ActionDescriptor.ActionName) filterContext.Controller.ViewData("debug") = String.Format("ここでデバッグ情報を表示します id:{0}", filterContext.RequestContext.RouteData.Values("id")) End Sub
引数で渡される ActionExecutedContext クラスには上記のような Controller プロパティなど様々な情報が入っているので、これを活用することができます。
このデバッグ情報をビューで表示させます。
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<dynamic>" %> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> Item </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2>Item</h2> <p><%: ViewData["debug"] %></p> </asp:Content>
この実行が下の図です。
このようにアクション フィルター内で、ViewData コレクションを使うと、ビューとのやり取りが簡単にできるので一時的な情報やデバッグ時の情報などが出しやすくなります。LINQ to Entities で生成される SQL 文などを表示させるとパフォーマンス調査などができるでしょう。
最後にユーザー情報を App_Data フォルダー内のローカル ファイルではなく、SQL Server 上に作成する場合の説明をしましょう。
Visual Studio 2010 で ASP.NET MVC アプリケーションを作成するとログイン情報は、App_Data フォルダー内にローカル ファイルとして保存されます。このデータは ASP.NET MVC アプリケーションでは利用できるのですが、バックアップの問題や、他の Web アプリケーションとの連携の問題が発生します。
このアカウント情報を SQL Server 上のデータベースに切り替えてみます。
まず、SQL Server Management Studio にアカウント情報を作成するデータベースを作ります。ここでは、「acldb」としています。
次に、データベースにアカウント情報用のテーブルとストアド プロシージャをインストールします。C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_regsql.exe を実行して、ウィザードに従ってテーブルを作成します。
ASP.NET MVC アプリケーションからアカウント用のデータベースにアクセスする設定は、web.config ファイルに記述されています。
<configuration> <connectionStrings> <add name="ApplicationServicesOrg" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" providerName="System.Data.SqlClient" /> <add name="ApplicationServices" connectionString="Server=localhost;Database=acldb;Integrated Security=true" providerName="System.Data.SqlClient" /> </connectionStrings>
接続情報の名前は、ApplicationServices となっているので、この接続文字列 (connectionString) に、SQL Server 上に作成したデータベースを指定します。
このようにすると、アカウント情報が SQL Server 上に作成されます。ローカル ファイルのデータベースと同様に ASP.NET Web サイト管理ツールを使ってユーザーとロールを管理できます。
いかがだったでしょうか。ASP.NET MVC のアクション フィルターは、あらかじめ定義されたものだけでなく独自に拡張できること理解頂けたと思います。C# や Visual Basic の属性の機能を使い、クラスやメソッド自身の情報としてアクション フィルターの動作を記述します。メソッドの実行前後のアスペクト指向的なインターフェースを使って、デバッグログだけでなく実行時のジャーナル機能 (誰がいつアクセスしたかなどの監査機能) を簡単に実装することができます。コード内で条件分岐をして if 文を使うよりも簡素に書けますので、ぜひ活用してみてください。
|