Skip to content
This repository has been archived by the owner on Feb 20, 2021. It is now read-only.

Latest commit

 

History

History

ASP.NET MVC アプリケーション開発入門 第 8 回 Action Filter について

ASP.NET MVC アプリケーション開発入門: 第 8 回 Action Filter について

License

  • Apache License, Version 2.0

Technologies

  • Visual Studio 2010
  • ASP.NET MVC

Topics

  • ASP.NET MVC アプリケーション

Updated

  • 06/07/2011

Description

執筆者: moonmile solutions 増田 智明

本連載では、日経 BP 社から発売された「ひと目でわかる ASP.NET MVC アプリケーション開発入門」をもとにして、執筆時に気づいたことや紙面の都合で書ききれなかった技術を紹介します。

目次

  1. はじめに
  2. Authorize 属性の動作をみる
  3. ログイン名によって制御する
  4. ロールを使う準備をする
  5. ロール名によって制御する
  6. 独自のアクション フィルターを作る
  7. ユーザー情報を SQL Server 上に作成する
  8. おわりに

1. はじめに

今回は、ASP.NET MVC のアクション メソッドを拡張するアクション フィルターについて解説します。アクション フィルターは、メソッドの属性として設定されます。クラスの静的なデータとして設定されるため、クラス内の共通処理を行う場合に使うことができます。

Visual Studio 2010 で ASP.NET MVC アプリケーションを作ると、ログイン機能がテンプレートとして付いてきますが、ログインを制御するのは AccountController クラスで、Authorize という属性が設定されています。この Authorize 属性の中でログインが行われているか否かを判断して、アクション メソッドの動作を制御しています。

アクション フィルターは、Authorize 属性の他に HandleError 属性、OutputCache 属性などがあります。

ページのトップへ


2. Authorize 属性の動作をみる

最初にログイン制御で使われる Authorize 属性の動作をみていきましょう。

Visual Studio 2010 で「ASP.NET MVC 2 Webアプリケーション」を作成します。

まず、HomeController.cs (VB の場合は HomeController.vb)を開いて、次の 2 つのメソッドを追加します。

C#
スクリプトの編集|{#scriptcode_dlg.remove_script}
csharp
public ActionResult Normal() 
{ 
    return View(); 
} 
 
[Authorize] 
public ActionResult LoginUser() 
{ 
    return View(); 
}
 
Visual Basic
スクリプトの編集|{#scriptcode_dlg.remove_script}
vb
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 の例を示します。

C#
スクリプトの編集|{#scriptcode_dlg.remove_script}
csharp
<%@ 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>
 
Visual Basic
スクリプトの編集|{#scriptcode_dlg.remove_script}
vb
<%@ 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 に次のようなリンクを作成しておきます。

C#
スクリプトの編集|{#scriptcode_dlg.remove_script}
csharp
<ul> 
<li><%: Html.ActionLink("一般ユーザー用""Normal")%></li> 
<li><%: Html.ActionLink("ログインユーザー用","LoginUser") %></li> 
</ul>

以下が実行した結果です。

図 1

動作を確認してみると、ログインしていない状態で、「ログイン ユーザー用」のリンクをクリックすると、ログインするビューにリダイレクトされます。これは、LoginUser アクション メソッドに設定されている Authorize 属性がログイン状態をチェックして、ログインのためのページにジャンプさせているためです。

ログイン制御のジャンプ先は、web.config ファイルに記述されています。

XML
スクリプトの編集|{#scriptcode_dlg.remove_script}
xml
    <authentication mode="Forms"      <forms loginUrl="~/Account/LogOn" timeout="2880" /> 
    </authentication>

この loginUrl 属性の値を変えることで、エラー時のジャンプ先のビューを変更することができます。例えば、Home/Error.aspx のビューにジャンプさせる時には次のように loginUrl 属性の値を変更します。

XML
スクリプトの編集|{#scriptcode_dlg.remove_script}
xml
    <authentication mode="Forms"      <forms loginUrl="~/Home/Error" timeout="2880" /> 
    </authentication>

図 2

ページのトップへ


3. ログイン名によって制御する

ユーザーがログインしているかどうかでチェックができたので、今度はログイン名によってログインの制御をしてみましょう。

まずは、AdminUser アクション メソッドを作成します。

C#
スクリプトの編集|{#scriptcode_dlg.remove_script}
csharp
[Authorize(Users="admin")] 
public ActionResult AdminUser() 
{ 
    return View(); 
}
 
Visual Basic
スクリプトの編集|{#scriptcode_dlg.remove_script}
vb
<Authorize(Users:="admin")> 
Public Function AdminUser() As ActionResult 
    Return View() 
End Function

Authorize 属性の Users パラメーターにログイン名を指定します。
こうすることで、特定のユーザーだけで表示できるページを作成することができます。

次に「admin」という名前のログイン名を作成しておいて、この admin ユーザーだけが表示できる AdminUser.aspx ビューを作ります。

C#
スクリプトの編集|{#scriptcode_dlg.remove_script}
csharp
<%@ 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 のページを表示できます。

図 3

図 4

ページのトップへ


4. ロールを使う準備をする

Authorize 属性では、ロール (役割) を使ったログインの制御も可能です。

ASP.NET MVC アプリケーションで扱う、ユーザー名とロールの関係は次の図になります。

図 5

ユーザー名で全てを制御しようとすると、管理ユーザーを追加するたびに属性を書き替えないといけません。これを避けるために、管理ロールを作成して、追加したい管理ユーザーを管理ロールに属するように設定します。

このロールを使うためには、まず Visual Studio 2010 の「プロジェクト」メニューから「ASP.NET 構成」を選択して、ASP.NET Web サイト管理ツールを開きます。
サイト管理ツールはブラウザー上で、ASP.NET アプリケーションのユーザーの管理などができます。

図 6

ロールを使うためには、まず「セキュリティ」のリンクをクリックした後で、「ロールの有効化」をクリックします。

「ロールの作成または管理」のリンクをクリックすると、新しいロールが作成できます。

ここでは「administrators」というロールを作成します。

図 7

次にユーザー管理のページを開いて、admin ユーザーを administrators ロールに追加します。

これで準備は完了です。ここでは、masuda と admin というユーザーがありますが、admin だけが管理ロールの administrators に属しています。

ページのトップへ


5. ロール名によって制御する

まずは、ビューを表示するための AdminRole メソッドに、次のように Authorize 属性を追加します。

C#
スクリプトの編集|{#scriptcode_dlg.remove_script}
csharp
[Authorize(Roles = "administrators")] 
public ActionResult AdminRole() 
{ 
    return View(); 
}
 
Visual Basic
スクリプトの編集|{#scriptcode_dlg.remove_script}
vb
<Authorize(Roles:="administators")> 
Public Function AdminUser() As ActionResult 
    Return View() 
End Function

次に、administrators のロールに属しているユーザーだけが表示できるビューを作成していきましょう。AdminRole.aspx を次のように作成します。

C#
スクリプトの編集|{#scriptcode_dlg.remove_script}
csharp
<%@ 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>

これを実行すると次の図になります。

図 8

masuda ユーザーでログインした状態でも管理ロール用のページ AdminUser.aspx を表示することはできません。masuda ユーザーをログアウトして、admin ユーザーでログインし直すと、管理ロールのページを表示することができます。

ページのトップへ


6. 独自のアクションフィルターを作る

ここまで既に ASP.NET MVC で用意されている Authorize 属性の解説をしてきましたが、独自のアクション フィルターを作ることもできます。

アクション フィルターは、属性としてアクション メソッドに設定して動作するためのインターフェースが用意されています。

  • IActionFilter

    アクション メソッドの実行前と実行後にフィルターを実行します。

  • IAuthorizationFilter

    アクション メソッドの実行前にフィルターを実行します。

  • IExceptionFilter

    例外がスローされると、フィルターが実行します。

  • IResultFilter

    アクションの結果の処理前と処理後にフィルターが実行します。

ここでは、IActionFilter インターフェースを使って、アクション メソッドの前後にログを出力できるようにしてみましょう。

C#
スクリプトの編集|{#scriptcode_dlg.remove_script}
csharp
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 ); 
    } 
}
 
Visual Basic
スクリプトの編集|{#scriptcode_dlg.remove_script}
vb
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 属性などと同じようにアクション メソッドに設定します。

C#
スクリプトの編集|{#scriptcode_dlg.remove_script}
csharp
[MyFilter] 
public ActionResult Item(int id) 
{ 
    return View(); 
}
 
Visual Basic
スクリプトの編集|{#scriptcode_dlg.remove_script}
vb
<MyFilter()> 
Public Function Item(ByVal id As IntegerAs ActionResult 
    Return View() 
End Function

Item.aspx ビューを作成して実行すると、Visual Studio 2010 の出力ウィンドウのログ出力が得られます。

図 9

IActionFilter インターフェースの OnActionExecuted メソッドでは、アクション メソッドの結果を修正することもできます。たとえば、ViewData プロパティを使い、デバッグ情報をビューに表示することも可能です。

アクション フィルターを次のように、ViewData を使うように書き変えます。

C#
スクリプトの編集|{#scriptcode_dlg.remove_script}
csharp
public void OnActionExecuted(ActionExecutedContext filterContext) 
{ 
    Debug.Print("メソッド後:{0}", 
        filterContext.ActionDescriptor.ActionName ); 
    filterContext.Controller.ViewData["debug"] =  
        string.Format("ここでデバッグ情報を表示します id:{0}", 
        filterContext.RequestContext.RouteData.Values["id"] ); 
}
 
Visual Basic
スクリプトの編集|{#scriptcode_dlg.remove_script}
vb
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 プロパティなど様々な情報が入っているので、これを活用することができます。

このデバッグ情報をビューで表示させます。

C#
スクリプトの編集|{#scriptcode_dlg.remove_script}
csharp
<%@ 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>

この実行が下の図です。

図 10

このようにアクション フィルター内で、ViewData コレクションを使うと、ビューとのやり取りが簡単にできるので一時的な情報やデバッグ時の情報などが出しやすくなります。LINQ to Entities で生成される SQL 文などを表示させるとパフォーマンス調査などができるでしょう。

ページのトップへ


7. ユーザー情報を SQL Server 上に作成する

最後にユーザー情報を App_Data フォルダー内のローカル ファイルではなく、SQL Server 上に作成する場合の説明をしましょう。

Visual Studio 2010 で ASP.NET MVC アプリケーションを作成するとログイン情報は、App_Data フォルダー内にローカル ファイルとして保存されます。このデータは ASP.NET MVC アプリケーションでは利用できるのですが、バックアップの問題や、他の Web アプリケーションとの連携の問題が発生します。

このアカウント情報を SQL Server 上のデータベースに切り替えてみます。

まず、SQL Server Management Studio にアカウント情報を作成するデータベースを作ります。ここでは、「acldb」としています。

図 11

次に、データベースにアカウント情報用のテーブルとストアド プロシージャをインストールします。C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_regsql.exe を実行して、ウィザードに従ってテーブルを作成します。

図 12

ASP.NET MVC アプリケーションからアカウント用のデータベースにアクセスする設定は、web.config ファイルに記述されています。

XML
スクリプトの編集|{#scriptcode_dlg.remove_script}
xml
<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 サイト管理ツールを使ってユーザーとロールを管理できます。

ページのトップへ


8. おわりに

いかがだったでしょうか。ASP.NET MVC のアクション フィルターは、あらかじめ定義されたものだけでなく独自に拡張できること理解頂けたと思います。C# や Visual Basic の属性の機能を使い、クラスやメソッド自身の情報としてアクション フィルターの動作を記述します。メソッドの実行前後のアスペクト指向的なインターフェースを使って、デバッグログだけでなく実行時のジャーナル機能 (誰がいつアクセスしたかなどの監査機能) を簡単に実装することができます。コード内で条件分岐をして if 文を使うよりも簡素に書けますので、ぜひ活用してみてください。


Code Recipe Code Recipe

ページのトップへ