更新日: 2010 年 4 月 9 日

C# の内容はこちらに掲載しています。10 行でズバリ!! URL の書き換えを行う (C#)

このコンテンツのポイント

  • ルーティングを利用した URL 設計の基礎を理解

今回紹介するコード

<Global.asax>

Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
 
    routes.MapRoute( _
       "Blog", _
       "Blog/{day}/{month}/{year}", _
       New With {
           .controller = "Article", _
           .action = "Index", _
           .day = DateTime.Now.Day, _
           .month = DateTime.Now.Month, _
           .Year = DateTime.Now.Year _
        }, _
        New With { _
           .day = "\d{1,2}", _
           .month = "\d{1,2}", _
           .year = "\d{4}" _
         } _
    )
 
    routes.MapRoute( _
        "Default", _
        "{controller}/{action}/{id}", _
        New With {.controller = "Home", .action = "Index", .id = ""} _
    )
 
End Sub

 

<ArticleController.vb>

Visual Basic
Public Class ArticleController
    Inherits System.Web.Mvc.Controller

    '
    ' GET: /Article

    Function Index(ByVal day As Integer, ByVal month As Integer, ByVal year As Integer) As ActionResult
        Dim msg = String.Format("指定日:{0}/{1}/{2}", year, month, day)
        Return Content(msg)
    End Function

End Class
 

 

目次

  1. はじめに
  2. デフォルト ルートの確認
  3. 自作ルートの追加
  4. コントローラー クラスの作成
  5. ASP.NET MVC アプリケーションの実行
  6. 制約条件の追加
  7. 参考: さまざまな URL パターン

1. はじめに

リクエストされた URL に応じて、実際の処理を行うべきコントローラー クラスを決定すること、またはその仕組みのことをルーティングといいます。ASP.NET MVC では、このルーティング機能を利用することで、物理的なファイル構造に依存しない URL 構造を設計できます。

たとえば、従来であれば「/App1/Contents」フォルダ配下の Details.aspx に対し、クエリ情報として、year、month、day などの情報を渡したい場合、以下のような URL で呼び出すのが一般的でした。

http://sample.site/App1/Contents/Details.aspx?year=2010&month=12&day=4

しかし、ルーティング機能を利用することで、(たとえば) 以下のような URL で呼び出せるようになります。

http://sample.site/Blog/4/12/2010

ルーティングはリクエスト URL を短くするというだけではありません。エンド ユーザーにとってよりわかりやすい URL の設計を可能にしてくれるのです。本稿では、デフォルトで定義されているルーティング設定 (ルート) を確認するとともに、自分でルートを定義する方法について解説します。

ページのトップへ


2. デフォルト ルートの確認

まずは、自動的に作成されるデフォルト ルートを確認しておきます。

ここでは、「10 行でズバリ !! ASP.NET MVC を構成する各コンポーネントとネーミング ルール (VB)」で作成した Visual Studio プロジェクト (手順通りであれば、MvcBeginVb プロジェクト) を開きます。

プロジェクトの直下に、グローバル アプリケーション クラス (Global.asax) が作成されているはずなので、これをコード エディターで開いてください。以下のようなコードが確認できるはずです (日本語のコメントは著者が追加したものです)。

Public Class MvcApplication
    Inherits System.Web.HttpApplication
 
    Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
 
        ' (2) デフォルトとなる Default ルートを登録
        routes.MapRoute( _
            "Default", _
            "{controller}/{action}/{id}", _
            New With {.controller = "Home", .action = "Index", .id = ""} _
        )
 
    End Sub
 
    Sub Application_Start()
        AreaRegistration.RegisterAllAreas()
 
        ' (1) アプリケーション起動時にルートを登録
        RegisterRoutes(RouteTable.Routes)
    End Sub
End Class

 

(1) の Start イベント ハンドラーで呼び出されている RegisterRoutes メソッドは、アプリケーション開始のタイミング で実行されるメソッドで、ルートをアプリケーションに登録します。新しいルートを追加するには、(2) のように、ルートのコレクションを表す RouteCollection オブジェクトに対して MapRoute メソッドを呼び出します。MapRoute メソッドの引数には、先頭から以下の順でルートに必要な情報を指定します。

  • ルートの名前
  • URL パターン
  • デフォルト値

つまり、上記のコードでは「{controller}/{action}/{id}」という URL パターンの Default ルートを追加しているわけです。{名前} は変数のプレイス ホルダーで、{controller} はコントローラー名、{action} はアクション名、そして、{id} は任意のパラメーター値を表します。パラメーター値を受け取る方法については、後で紹介します。

これら変数のデフォルト値は、MapRoute メソッドの第 3 引数で指定できます。Default ルートの場合、それぞれの変数には以下のようなデフォルト値が定義されています。

変数 デフォルト値
controller Home
action Index
id (空文字列)

表 1 Default ルートのデフォルト値

これまで、たとえば「http://localhost:49870/Address/Create」のような URL (ポート番号は環境によって異なります) で、Address コントローラーの Create アクションを呼び出せていたのは、このような Default ルートがあらかじめで用意されていたためです。

ここでいったんアプリケーションを起動してみましょう。Visual Studio で [F5] キーを押すと、開発サーバーとブラウザーが起動し、「http://localhost:49870」のような URL でトップ ページが表示されます。

図 1 ASP.NET MVC プロジェクトのトップ ページ

この場合の「http://localhost:49870/」では、{controller}、{action}、{id} ともに省略されていますので、あらかじめ定義されたデフォルト値が採用されて、Home コントローラーの Index アクションが呼び出されます。なお、Home コントローラーは、プロジェクトにデフォルトで用意されているコントローラーです。また、「http://localhost:49870」は、「http://localhost:49870/Home」、「http://localhost:49870/Home/Index」と指定しても同じ意味です。

ここまでを確認できたら、ブラウザーを終了し、デバッグ実行を終了します。

ページのトップへ


3. 自作ルートの追加

デフォルトで定義されているルートの挙動を理解できたところで、「http://sample.site/Blog/4/12/2010」のような URL で Article コントローラーの Index アクションを呼び出すような「Blog」ルートを例として作成してみましょう。

ソリューション エクスプローラーから Global.asax を開き、最終的に次のようなコードになるように、コード エディターで入力/編集を行ってください。太字の部分が実際に入力する必要のあるコードです。

Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}")

    routes.MapRoute( _
        "Blog", _
        "Blog/{day}/{month}/{year}", _
'(1) URL パターン
        New With {
            .controller = "Article", _
'(2) コントローラー / アクションのデフォルト値を指定
            .action = "Index", _
            .day = DateTime.Now.Day, _
'(3) 年月日のデフォルト値を指定
            .month = DateTime.Now.Month, _
            .Year = DateTime.Now.Year _
        }, _
    )

    routes.MapRoute( _
        "Default", _
        "{controller}/{action}/{id}", _
        New With {.controller = "Home", .action = "Index", .id = ""} _
    )

End Sub

(1) のように、URL パターンは、必ずしも {controller} や {action} を含んでいなくても構いません。ただし、その場合には {controller}、{action} のデフォルト値が必須となります (さもないと、呼び出すべきコントローラー クラスとアクション メソッドが決まらないからです)。(2) では、それぞれ

  • {controller} = Article
  • {action} = Index

となるようにデフォルト値を設定していますので、「/Blog/...」でアクセスされた場合には、つねに Article コントローラーの Index アクションが呼び出されるようになります。

この Blog ルートでは {day}、{month}、{year} といったパラメーターも定義しています。(3) では、それぞれのパラメーターに対して、現在の年月日をデフォルト値として設定しています。このように、デフォルト値には固定値だけではなく、式を指定しても構いません。これによって、たとえば今日の日付が 2010 年 6 月 25 日であれば、「/Blog/25/」は「/Blog/25/06/2010」と同じ意味になります。

ページのトップへ


4. コントローラー クラスの作成

Blog ルートの挙動を確認するために、Article コントローラーの Index アクションを用意します。コントローラー クラスを作成するには、ソリューション エクスプローラーから「/Controllers」フォルダーを右クリックし、表示されたコンテキスト メニューから [追加] - [Controller...] を選択します。

[Add Controller] ダイアログが表示されますので、[Controller Name] (コントローラー名) に「ArticleController」と入力します。今回は [Add action methods for Create, Update and Details scenarios] (データの作成、更新、詳細シナリオのためのアクションを追加する) にはチェックは入れません。

図 2 [Add Controller] ダイアログ

[OK] ボタンをクリックすると、コード エディターに ArticleController.vb の骨組みとなるコードが表示されます。ここでは簡単な Index メソッドを記述します。最終的に次のようなコードになるように、コード エディターで入力/編集を行ってください。太字の部分が実際に入力する必要のあるコードです。

Namespace MvcBeginVb
    Public Class ArticleController
        Inherits System.Web.Mvc.Controller

        '
        ' GET: /Article

        Function Index(ByVal day As Integer, ByVal month As Integer, ByVal year As Integer) As ActionResult
            Dim msg = String.Format("指定日:{0}/{1}/{2}", year, month, day)

            ' (1) 指定された文字列をそのまま出力
            Return Content(msg)
        End Function

    End Class
End Namespace

ルートで定義されたパラメーターを取得するには、アクション メソッドにルート パラメーターと同名の引数を用意するだけです。これによって、対応するパラメーター値が自動的にセットされます。

本来であれば、取得したパラメーターに基づいてデータベースの検索などを行うべきところですが、ここでは最低限、パラメーター値を表示するに留めます。(1) の Content メソッドは、指定された文字列を (ビュー スクリプトなどを使わずに) そのまま出力するためのメソッドです。MVC の考え方からすれば、出力にはビュー スクリプトを介するべきですが、ここでは最小限の動作確認のために、ビュー スクリプトは省略しています。

ページのトップへ


5. ASP.NET MVC アプリケーションの実行

それでは Visual Studio で [F5] キーを押して、いま作成したアプリケーションを実行してみましょう。開発サーバーと Web ブラウザーが起動し、トップ画面が表示されますので、Web ブラウザーのアドレス欄から「http://localhost:49870/Blog/14/5」のように入力してください。以下の図のように、取得したパラメーター値が表示されるはずです。

図 3 取得したルート パラメーターを表示

ページのトップへ


6. 制約条件の追加

次に、Blog ルートのパラメーターに、以下のような制約条件を追加してみましょう。

  • {year} パラメーターは 4 桁の数値
  • {month}、{day} は 1 ~ 2 桁の数値

制約条件を設定することで、たとえば「http://localhost:49870/Blog/X/Y/Z」のような、意図しないパラメーター値をあらかじめ除外できます。予期せぬ例外を未然に防ぐという意味でも、外部から受け取る値の範囲はできるだけ制限しておくべきです。

制約条件を追加するには、ソリューション エクスプローラーから Global.asax を開き、MapRoute メソッドに引数を追加します。最終的に次のようなコードになるように、コード エディターで入力/編集を行ってください。太字の部分が実際に入力する必要のあるコードです。

Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}")

    ...中略...

    routes.MapRoute( _
        "Blog", _
        "Blog/{day}/{month}/{year}", _
        New With {
            .controller = "Article", _
            .action = "Index", _
            .day = DateTime.Now.Day, _
            .month = DateTime.Now.Month, _
            .Year = DateTime.Now.Year _
        }, _
        New With { _
            .day = "\d{1,2}", _
            .month = "\d{1,2}", _
            .year = "\d{4}" _
        }
_
    )
End Sub

制約条件は、MapRoute メソッドの 4 番目の引数として指定できます。「パラメーター名 = 正規表現パターン」の形式からなる匿名型のオブジェクトとして表現します。@"\d{1,2}" では、「\d」が任意の数値を、{1,2} はその数値が 1 ~ 2 桁の間であることを意味します。

以上を理解したら、もう一度、アプリケーションを起動してみましょう。ブラウザーのアドレス欄から「http://localhost:49870/Blog/X/Y/Z」のような URL を入力すると、以下のようなエラー画面が表示されることを確認してください。

図 4 ルートの制約条件にマッチしないので、エラー画面が表示される

制約条件によって、{year}、{month}、{day} の値が数値以外である場合に Blog ルートは適用されなくなったためです。

ページのトップへ


7. 参考: さまざまな URL パターン

自分でルートを定義することで、物理的なファイル構造や、コントローラー/アクションの名前から完全に独立した、エンド ユーザーにとってわりやすい URL を設計できることがおわかりになったと思います。最後に、ルートの定義で利用できる URL パターンの例をいくつか挙げておきます。

No. URL パターン マッチするリクエスト URL の例
1 {controller}/{action}/{group}/{id} /Product/Search/Book/9784774140766
2 Blog/{year}-{month}-{day} /Blog/2010-08-05
3 Blog/{*params} /Blog/2010/08/05、/Blog/Computer

2 の例のように、パラメーター同士は必ずしもスラッシュ (/) で区切らなくても構いません。「-」や「_」「.」などの任意の文字を利用できます。また、3 の {*名前} という表現は、それ以降のパラメーターを、スラッシュで区切られているかどうかに関わらず、すべて params という変数で受け取るという意味です。従って、Blog/{params} (アスタリスクなし) の場合には、「/Blog/Computer」という URL にはマッチしますが、「/Blog/2010/08/05」にはマッチしません。


Code Recipe .NET Framework デベロッパー センター

ページのトップへ