更新 : 2007 年 11 月
ASP.NET ルーティングを使用すると、Web サイトの特定のファイルにマップする必要のない URL を使用できます。URL をファイルにマップする必要がないため、ユーザーの操作を表すわかりやすい URL を Web アプリケーションで使用できます。
ルーティングを使用しない ASP.NET アプリケーションでは、一般に、受信した URL 要求はディスク上の物理ファイル (.aspx ファイルなど) にマップされます。たとえば、https://server/application/Products.aspx?id=4 への要求は、ブラウザへの応答をレンダリングするためのコードとマークアップを含む Products.aspx という名前のファイルにマップされます。この Web ページは、id=4 というクエリ文字列値を使用して表示するコンテンツの種類を決定しますが、この値はユーザーにとってほとんど意味を持ちません。
ASP.NET ルーティングでは、URL 要求を処理するときに使用される値のプレースホルダを含む URL パターンを定義します。実行時には、アプリケーション名に続く URL の一部が、定義した URL パターンに従って個別の値に解析されます。たとえば、https://server/application/Products/show/beverages という要求では、ルーティング パーサーから要求のハンドラに Products、show、および beverages という値を渡すことができます。これに対し、URL ルーティングによって管理されない要求の場合、/Products/show/beverages というフラグメントは、アプリケーションにおいてファイルのパスとして解釈されます。
URL パターンを使用すると、ルートに対応する URL をプログラムで作成することもできます。これにより、ハイパーリンクを作成するためのロジックを ASP.NET アプリケーションに集中化することができます。
ASP.NET ルーティングと URL の書き換え
ASP.NET ルーティングは、他の URL の書き換え方式とは異なります。URL の書き換えでは、要求を受け取ると、その要求を Web ページに送信する前に URL が実際に変更されます。たとえば、URL の書き換えを使用するアプリケーションでは、/Products/Widgets/ という URL が /Products.aspx?id=4 に変更されます。さらに、通常、URL の書き換えでは、パターンに基づいて URL を作成するための API は用意されません。URL の書き換えの場合、URL パターンを変更するには、元の URL を含むすべてのハイパーリンクを手動で更新する必要があります。
ASP.NET ルーティングでは、ルーティングによって URL から値を抽出できるため、受け取った要求が処理されるときに URL は変更されません。URL を作成する必要がある場合は、URL を生成するメソッドにパラメータ値を渡します。URL パターンを変更する場合は、1 か所を変更するだけで、そのパターンに基づいてアプリケーション内で作成されるすべてのリンクが、新しいパターンを自動的に反映するようになります。
URL ルートの定義
定義された URL パターンは、ルートと呼ばれます。ルートには、URL 要求から解析される値にマップされるプレースホルダを指定します。また、URL 要求の照合に使用される定数値を指定することもできます。
ルートには、プレースホルダ (URL パラメータと呼ばれます) を中かっこ ({ と }) で囲んで定義します。/ 文字は、URL の解析時に区切り記号として解釈されます。ルート定義において、中かっこで囲まれていない区切り記号以外の情報は、定数値として扱われます。区切り記号の間から抽出された値がプレースホルダに割り当てられます。
区切り記号の間には複数のプレースホルダを定義できますが、その場合は定数値で区切る必要があります。たとえば、{language}-{country}/{action} は有効なルート パターンです。しかし、{language}{country}/{action} は、プレースホルダの間に定数も区切り記号もないので、有効なパターンではありません。したがって、language プレースホルダの値と country プレースホルダの値をどこで区切ればよいかを判断できません。
次の表は、有効なルート パターンと、各パターンに一致する URL 要求の例を示しています。
ルート定義 |
一致する URL の例 |
---|---|
{controller}/{action}/{id} |
/Products/show/beverages |
{table}/Details.aspx |
/Products/Details.aspx |
blog/{action}/{entry} |
/blog/show/123 |
{reporttype}/{year}/{month}/{day} |
/sales/2008/1/5 |
{locale}/{action} |
/en-US/show |
{language}-{country}/{action} |
/en-US/show |
通常、各ルートは、Global.asax ファイル内の Application_Start イベントのハンドラから呼び出されるメソッド内に追加します。これにより、アプリケーションが起動したときにルートを使用できるようになります。さらに、アプリケーションの単体テストを行うときに、メソッドを直接呼び出すことも可能になります。アプリケーションの単体テストでメソッドを直接呼び出す場合、ルートを登録するメソッドは、RouteCollection パラメータを受け取る静的 (Visual Basic では Shared) メソッドである必要があります。
ルートを追加するには、RouteTable クラスの静的プロパティである Routes にルートを追加します。Routes プロパティは、ASP.NET アプリケーションのすべてのルートを格納する RouteCollection オブジェクトです。次の例は、Global.asax ファイルのコードの一部を示し、action および categoryName という名前の 2 つの URL パラメータを定義する Route オブジェクトを追加しています。
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
RegisterRoutes(RouteTable.Routes)
End Sub
Shared Sub RegisterRoutes(routes As RouteCollection)
Dim urlPattern As String
Dim categoryRoute As Route
urlPattern = "Category/{action}/{categoryName}"
categoryRoute = New Route(urlPattern, New CategoryRouteHandler)
routes.Add(categoryRoute)
End Sub
protected void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.Add(new Route
(
"Category/{action}/{categoryName}"
, new CategoryRouteHandler()
));
}
ルート パラメータの既定値の設定
ルートを定義するときに、パラメータの既定値を割り当てることができます。既定値は、そのパラメータの値が URL に含まれていない場合に使用されます。ルートの既定値を設定するには、Route クラスの Defaults プロパティにディクショナリを割り当てます。既定値が設定されたルートを次の例に示します。
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
RegisterRoutes(RouteTable.Routes)
End Sub
Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
Dim urlPattern As String
Dim categoryRoute As Route
urlPattern = "Category/{action}/{categoryName}"
categoryRoute = New Route(urlPattern, New CategoryRouteHandler)
categoryRoute.Defaults = New RouteValueDictionary(New With _
{.categoryName = "food", _
.action = "show"} )
routes.Add(categoryRoute)
End Sub
void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.Add(new Route
(
"Category/{action}/{categoryName}"
new CategoryRouteHandler()
)
{
Defaults = new RouteValueDictionary
{{"categoryName", "food"}, {"action", "show"}}
}
);
}
ASP.NET ルーティングによって URL 要求が処理されるとき、この例のルート定義から、次の表に示すような結果が生成されます (categoryName の既定値は food、action の既定値は show になります)。
URL |
パラメータの値 |
---|---|
/Category |
action = "show" (既定値) categoryName = "food" (既定値) |
/Category/add |
action = "add" categoryName = "food" (既定値) |
/Category/add/beverages |
action = "add" categoryName= "beverages" |
可変数のセグメントの処理
可変数の URL セグメントを含む URL 要求を処理することが必要になる場合があります。ルートを定義するとき、最後のパラメータにアスタリスク (*) を付けると、そのパラメータを URL の残りの部分に一致させることができます。これは汎用的なパラメータと呼ばれます。汎用的なパラメータのあるルートは、最後のパラメータに該当する値を含まない URL にも一致します。可変数のセグメントに一致するルート パターンを次の例に示します。
query/{queryname}/{*queryvalues}
ASP.NET ルーティングによって URL 要求が処理されるとき、この例のルート定義から、次の表に示すような結果が生成されます。
URL |
パラメータの値 |
---|---|
/query/select/bikes/onsale |
queryname = "select" queryvalues = "bikes/onsale" |
/query/select/bikes |
queryname = "select" queryvalues = "bikes" |
/query/select |
queryname = "select" queryvalues = 空の文字列 |
ルートへの制約の追加
URL 内のパラメータ数によって URL をルート定義に一致させることに加え、パラメータ内の値が特定の制約に従うように指定することもできます。ルートの制約を満たさない値が URL に含まれている場合、そのルートは要求の処理に使用されません。制約を追加すると、アプリケーションで有効な値が URL パラメータに含まれることを保証できます。
制約は、正規表現を使用するか、または IRouteConstraint インターフェイスを実装するオブジェクトを使用して定義します。制約を追加するには、ルート定義を Routes コレクションに追加するときに、検証テストを含む RouteValueDictionary オブジェクトを作成します。次に、このオブジェクトを Constraints プロパティに割り当てます。ディクショナリのキーは、制約が適用されるパラメータを識別します。ディクショナリの値には、正規表現を表す文字列か、または IRouteConstraint インターフェイスを実装するオブジェクトを使用できます。
文字列を指定した場合、ルーティングはその文字列を正規表現として扱い、Regex クラスの IsMatch メソッドを呼び出して、パラメータ値が有効かどうかを確認します。この正規表現では、常に大文字と小文字は区別されません。詳細については、「.NET Framework の正規表現」を参照してください。
IRouteConstraint オブジェクトを指定した場合、ASP.NET ルーティングは、IRouteConstraint オブジェクトの Match メソッドを呼び出して、パラメータ値が有効かどうかを確認します。Match メソッドは、パラメータ値が有効かどうかを示すブール値を返します。
locale パラメータと year パラメータに含めることができる値を制限する制約を次の例に示します。
Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
Dim urlPattern As String
Dim reportRoute As Route
urlPattern = "{locale}/{year}"
reportRoute = New Route(urlPattern, New ReportRouteHandler)
reportRoute.Constraints = New RouteValueDictionary(New With _
{.locale = "[a-z]{2}-[a-z]{2}", .year = "\d{4}"})
routes.Add(reportRoute)
End Sub
void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.Add(new Route
(
"{locale}/{year}"
, new ReportRouteHandler()
)
{
Constraints = new RouteValueDictionary
{{"locale", "[a-z]{2}-[a-z]{2}"},{year, @"\d{4}"}}
});
}
ルーティングによって URL 要求が処理されるとき、前の例のルート定義から、次の表に示すような結果が生成されます。
URL |
結果 |
---|---|
/en-US |
一致なし。locale と year の両方が必要です。 |
/en-US/08 |
一致なし。year の制約により、4 桁の数字が必要です。 |
/en-US/2008 |
locale = "en-US" year = "2008" |
ルーティングが適用されないときのシナリオ
既定では、ルーティングは、Web サーバー上の既存の物理ファイルにマップされる要求を処理しません。たとえば、https://server/application/Products/Beverages/Coffee.aspx という要求は、Products/Beverages/Coffee.aspx に物理ファイルが存在すると、ルーティングによって処理されません。この場合、定義されているパターン ({controller}/{action}/{id} など) に要求が一致しても、ルーティングによる処理は行われません。
要求がファイルを指す場合でもすべての要求をルーティングで処理するには、RouteCollection オブジェクトの RouteExistingFiles プロパティを true に設定して、既定の動作を上書きします。この値を true に設定すると、定義されているパターンに一致するすべての要求がルーティングによって処理されます。
特定の URL 要求がルーティングで処理されないように指定することもできます。特定の要求がルーティングで処理されるのを防ぐには、ルートを定義し、StopRoutingHandler クラスを使用してそのパターンを処理するように指定します。StopRoutingHandler オブジェクトによって要求が処理される場合、StopRoutingHandler オブジェクトは、要求がそれ以上ルートとして処理されるのをブロックします。代わりに、その要求は、ASP.NET ページ、Web サービス、またはその他の ASP.NET エンドポイントとして処理されます。たとえば、次のルート定義を追加すると、WebResource.axd ファイルへの要求がルーティングによって処理されるのを防ぐことができます。
Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
routes.Add(New Route("{resource}.axd/{*pathInfo}", New StopRouteHandler()))
End Sub
public static void RegisterRoutes(RouteCollection routes)
{
routes.Add(new Route("{resource}.axd/{*pathInfo}", new StopRouteHandler()));
}
URL とルートの照合
URL 要求を処理するとき、ルーティングは要求の URL をルートに一致させようと試みます。URL 要求をルートに一致させる操作は、次のすべての条件に依存します。
ユーザーが定義したルート パターン、またはプロジェクトの種類に含まれている既定のルート パターン (存在する場合)
Routes コレクションに追加した順序
ルートに指定した既定値
ルートに指定した制約
物理ファイルに一致する要求を処理するようにルーティングが定義されているかどうか
不適切なハンドラによって要求が処理されるのを避けるために、ルートを定義するときは、これらのすべての条件を考慮する必要があります。Routes コレクション内での Route オブジェクトの順序は重要です。ルートの照合は、コレクション内の最初のルートから最後のルートの順に試行されます。一致が見つかると、それ以降のルートは評価されません。通常、Routes プロパティにルートを追加するときは、最も特定性の高いルート定義から最も特定性の低いルート定義の順に追加します。
たとえば、次のパターンでルートを追加するとします。
ルート 1: {controller}/{action}/{id}
ルート 2: products/show/{id}
最初にルート 1 が評価されますが、ルート 2 に一致する要求はすべてルート 1 にも一致するため、ルート 2 による要求の処理はまったく行われません。https://server/application/products/show/bikes への要求は、ルート 2 の方が一致度が高いと考えられますが、ルート 1 によって次の値として処理されます。
controller = products
action = show
id = bikes
要求にパラメータが含まれていない場合は、既定値が使用されます。その結果、ルートが予想外の要求に一致する場合があります。たとえば、次のパターンでルートを追加するとします。
ルート 1: {report}/{year}/{month} (year と month に既定値を設定)
ルート 2: {report}/{year} (year に既定値を設定)
この場合、ルート 2 で要求が処理されることはありません。ルート 1 は月次レポートを想定し、ルート 2 は年次レポートを想定しているとします。ただし、ルート 1 に既定値を設定すると、ルート 2 として解釈できる要求が、すべてルート 1 にも一致するようになります。
annual/{report}/{year} や monthly/{report}/{year}/{month} のように定数を含めることで、パターンのあいまいさを避けることができます。
RouteTable コレクションに定義されているどの Route オブジェクトにも URL が一致しない場合、その要求は ASP.NET ルーティングでは処理されません。代わりに、ASP.NET ページ、Web サービス、またはその他の ASP.NET エンドポイントに処理が渡されます。
ルートからの URL の作成
URL を作成するロジックを 1 か所にまとめる必要がある場合は、ルートを使用して URL を生成できます。URL を作成するには、RouteCollection オブジェクトの GetVirtualPath メソッドに、パラメータ値をディクショナリとして渡します。GetVirtualPath メソッドは、ディクショナリ内のパラメータに一致する最初のルートを RouteCollection オブジェクトから検索します。一致したルートを使用して、URL が生成されます。ルート定義の例を次に示します。
Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
routes.Add(New Route( _
"Category/{action}/{categoryName}", _
New RouteValueDictionary(New With _
{.categoryName = "food", _
.action = "show"}), _
New CategoryRouteHandler()) )
End Sub
public static void RegisterRoutes(RouteCollection routes)
{
routes.Add(new Route
(
"Category/{action}/{categoryName}"
new CategoryRouteHandler()
)
{
Defaults = new RouteValueDictionary {{"categoryName", "food"},
{"action", "show"}}
}
);
}
このルートに基づいて URL を作成するコントロールを次の例に示します。
Dim urlParameters As RouteValueDictionary
urlParameters = New RouteValueDictionary(New With {.categoryName = "beverages", _
.action = "summarize"})
HyperLink1.href = RouteTable.Routes.GetVirtualPath _
(context, urlParameters).VirtualPath
HyperLink1.href = RouteTable.Routes.GetVirtualPath
(context,
new RouteValueDictionary {
{ "categoryName", "beverages" },
{"action", "summarize" }}
).VirtualPath;
このコードを実行すると、HyperLink1 コントロールの href プロパティに値 "Category/summarize/beverages" が格納されます。
ルートから URL を作成するときには、ルートの名前を含めることで、どのルートを使用するかを指定できます。詳細については、「方法 : ルートから URL を構築する」を参照してください。