読者です 読者をやめる 読者になる 読者になる

miso_soup3 Blog

主に ASP.NET 関連について書いています。

ASP.NET MVC RouteLink 使い方

ASP.NET MVC 深夜連絡 ASP.NET MVC Advent Calendar 2013

by 深夜連絡 ASP.NET MVC な Web アプリ Advent Calendar 2013 11 日目

ASP.NET MVC では、リンクを貼るための HTML ヘルパーメソッドとして @Html.ActionLink(...) の他に @Html.RouteLink(...) を使うことができます。今回は後者の RouteLink について、基本的な使い方を説明します。サンプルは 次回 にて。

  • ActionLink と RouteLink の違い
  • ルート名で
  • ルート名とパラメーターで
  • Root URL へのリンク "Default" でよくある間違い

ActionLink と RouteLink の違い

ActionLink は、コントローラー・アクション等を指定してリンクを生成するのに対し、RouteLink は、ルートを指定してリンクを生成します。

ルートを指定する、というのはルーティング設定(例:RouteConfig.cs)で定義されているルートを指定する、ということです。

ASP.NET MVC では、ActionLink だけでも十分に通用しますが、RouteLink を活用することで効率良く開発できる場合があります。活用方法については、次回で紹介します。

ルート名で

ルート名を指定してリンクを貼る方法です。

ルーティング設定が、↓のように定義されていたとします。

using System.Web.Mvc;
using System.Web.Routing;

namespace WebApplication1
{
	public class RouteConfig
	{
		public static void RegisterRoutes(RouteCollection routes)
		{
			routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
			//↓このルートへのリンクをはる
			routes.MapRoute(
				name: "RouteContactMe",
				url: "Contact/Me",
				defaults: new { controller = "Home", action = "Contact" }
			);

			routes.MapRoute(
				name: "Default",
				url: "{controller}/{action}/{id}",
				defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
			);
		}
	}
}

定義されている URL「Contact/Me」にリンクを貼るには、↓のようにルート名(ルーティング設定にて name で指定した値= RouteContactMe)を指定します。

@Html.RouteLink("Contact Me", "RouteContactMe")

生成される HTML:

<a href="/Contact/Me">Contact Me</a>

もし、属性ルーティングを使用している場合は↓のようにルート名の定義を追加することで、先ほどの RouteLink でリンクを貼ることができます。

public class HomeController : Controller
{
	[Route("Contact/Me", Name = "RouteContactMe")]
	public ActionResult Contact();
}

ルート名とパラメーターで

↓のように、ルーティング設定にて URL「Contact/Me/{name}」というようにパラメーターを定義している場合は、

routes.MapRoute(
	name: "RouteContactMe",
	url: "Contact/Me/{name}",
	defaults: new { controller = "Home", action = "Contact", name = "defaultName" }
);

↓のように、パラメーターを指定することができます。

@Html.RouteLink("Contact Me", "RouteContactMe", new { name = "taro" })

生成される HTML:

<a href="/Contact/Me/taro">Contact Me</a>

(ちなみに、このときパラメーターを指定しないで @Html.RouteLink("Contact Me", "RouteContactMe") とかくと、「/Contact/Me」という URL でリンクの HTML が生成されます。リンクの HTML 生成時は、ルーティング設定で定義した規定値「defaultName」は反映されません。)

Root URL へのリンク "Default" でよくある間違い

よくある間違いは、URL「http://localhost/」(Root URL)といったトップページへのリンクを RouteLink で貼ろうとしたときに起こります。

デフォルトの MVC プロジェクトでは、ルーティング設定は以下のように定義されており、URL「http://localhost/」でアクセスした場合は、コントローラーは「Home」アクションは「Index」にマッピングされるようになっています。

routes.MapRoute(
	name: "Default",
	url: "{controller}/{action}/{id}",
	defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

そして必要があれば、このコントローラーとアクションの指定を、自分のアプリのトップページを表示するアクションに変更します。
次に、とある場所でトップページへのリンクを貼ります。↓のように RouteLink を使って。

@Html.RouteLink("トップへ戻る", "Default")

ですがこの場合、生成される URL は期待している URL「http://localhost/」ではありません。おそらく現在の URL が生成され、クリックしてもトップページに遷移することはできません。
(URL「~/Diary/Details」のビューにてリンクを貼った場合、「~/Diary/Details」のリンクが生成される)

対策

RouteLink を使ってトップページへリンクを貼る場合は、↓のようにルーティング設定に”新たに”ルートを追加し、

routes.MapRoute(
	name: "Root",
	url: "",
	defaults: new { controller = "Home", action = "Index" }
);

ルート名を指定して RouteLink を使用します。

@Html.RouteLink("トップへ戻る", "Root")

(RouteLink にこだわらずとも、ActionLink や 独自のヘルパーを使う、という手段もありますが)

原因

ここからは基本ではなく Deep Dive な話になります。

正しくリンクが貼れなかった原因は、ルーティング設定の URL にて定義されている「{name}」といったパラメーターの値の解決方法(RouteLink メソッド内におけるロジック)にあります。

RouteLink は 対象ルートの URL にパラメーターが定義されている場合、

  • 1. RouteLink メソッドの引数にて指定されたパラメーターの値
  • 2. 現在のルートのパラメーターの値

の優先順番で値を解決します。(厳密にいうと、2. に 1. を上書きしてる)

"Defaullt"のルートの URL「{controller}/{action}/{id}」にはパラメーターが 3 つ定義されています。↓のようにリンクを貼った場合、

@Html.RouteLink("トップへ戻る", "Default")

RouteLink メソッドの引数に値が指定されていないので、現在のルートのパラメーター値が採用されます。なので、現在のコントローラー(controller)とアクション(action)が反映された URL のリンクが貼られてしまいます。(id の値はこの時は指定されません。アクセスされたときに UrlParameter.Optional というオプション指定が参照されます。)

ルーティングの設定にて default 引数に定義しているパラメーターの初期値というのは、URL でアクセスされた時に参照されるのであって、RouteLink での URL 生成時には参照されません。

以上の仕組みにより、Root URL へのリンクは↓のように書くこともできます。(あまり使いたくない感じですが)

@Html.RouteLink("トップへ戻る2", new { controller = "", action = "" })

他、現在のコントローラーで別のアクションに遷移するリンクを↓のように書くこともできます。

@Html.RouteLink("他のアクションへ", new { action = "Contact" })
//@Html.ActionLink("他のアクションへ", "Contact") と一緒。

こう見ると RouteLink は複雑そうですが、通常は意識することはないと思います。