ASP.NET MVC 違うコントローラーへのリンクを貼る
by 深夜連絡 ASP.NET MVC な Web アプリ Advent Calendar 2013 10 日目
今回は、HTML ヘルパー:@Html.ActionLink で、違うコントローラーへのリンクを貼る方法についてです。陥りやすいミスについて少し。
違うコントローラーへリンクを貼る
違うコントローラーへのリンクとは、例えば「~/Books/Details」で表示されるページに、「~/Library/Details」といったリンクを貼ることです。
この場合、URL「~/Library/Details」へのリンクを貼るのは簡単です。↓のようにかきます。
(以降、デフォルトのルーティング設定(~/{controller}/{action}/{id})が適用されているものとします。)
@Html.ActionLink("Go to ~/Library/Details", "Details", "Library")
では、「~/Library/Details/4」のようにルートパラメーターを指定してリンクを貼る場合、どう書くでしょうか?
陥りやすい間違った貼り方・正しい貼り方
よくあるのが、↓のように @Html.ActionLink を間違った書き方で使ってしまうことです。
@Html.ActionLink("間違った例:Go to ~/Library/Details/4", "Details", "Library", new { id = 4 })
正しくは、↓のようにかきます。(2つあります)
@Html.ActionLink("正しい例1:Go to ~/Library/Details/4", "Details", new { id = 4, controller = "Library" }) @Html.ActionLink("正しい例2:Go to ~/Library/Details/4", "Details", "Library", new { id = 4 }, null)
なぜ前者ではダメなのか?
前者の間違った例でかくと、HTML は↓のように出力され、正しくリンクを貼ることができません。同じコントローラーへのリンク「/Book/Details?Length=7」に遷移してしまいます。
<a id="4" href="/Book/Details?Length=7">間違った例:Go to ~/Library/Details/4</a>
理由は、@Html.ActionLink のメソッドの引数の使い方が違うからです。間違った例で呼び出すメソッドの引数は、↓のように定義されています。
//@Html.ActionLink("間違った例:Go to ~/Library/Details/4", "Details", "Library", new { id = 4 }) //で呼び出されるメソッド: public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, object routeValues, object htmlAttributes);
(ActionLink は拡張メソッドなので、第1引数 htmlHelper は今はスルーしてください。)
@Html.ActionLink で指定した "Details" は object 型の routeValues として、「new { id = 4 }」は object 型の htmlAttributes として解釈されるため、正しくリンクを貼ることができません。
実際に、出力されている HTML に「<a id="4" ...」とあるように、htmlAttributes で指定した値が反映されています。
ちなみにリンク先の「~/Book/Details?Length=7」のクエリ文字列「Length=7」とは、string 型のインスタンスである "Details"を、キーと値のペア Dictionary に変換され、クエリ文字列として出力された結果になります。(この場合、キーはプロパティ名―つまり string 型のプロパティである Length になり、値はその値である 7 になります。)
ActionLink メソッド一覧
@Html.ActionLink はオーバーライドが複数あるため、複雑な設定をする時はちょっと注意が必要です。
(面倒ですけど)
(未だに action と controller どっちを先に書くべきだったか迷う)
@Html.ActionLink のよく使うであろうオーバーライド一覧をのせておきます。
正しいリンクの貼り方は、一覧のうち A, B を使ったものとなります。
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName); //↓ 正しい例1でよびだした public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, object routeValues); public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, RouteValueDictionary routeValues); public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName); public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, object routeValues, object htmlAttributes); public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes); //↓ 正しい例2でよびだした public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes); public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes);
(他にもまだある。)
サンプル
最後に、ActionLink を検証するサンプルの実装方法を紹介して終わります。
こんな↓ふうに、リンクを出力し、実際に出力される HTML を確かめることができます。
Razor はこちら↓
プロジェクト用意
準備
プロジェクトの用意
Visual Studio のサイト から、Visual Studio Express 2013 for Web(以下、VS Express 2013)をインストールします。無償です。
VS Express 2013 を開き、メニューから ファイル>新規作成>プロジェクト を選択します。
「ASP.NET Web アプリケーション」を選択し、OK します。
コントローラーの用意
BookController クラスと LibraryController の2つを用意します。
BookController.cs :
using System.Web.Mvc; namespace WebApplication33.Controllers { public class BookController : Controller { // GET: /Book/ public ActionResult Details(int? id) { return View(); } } }
LibraryController.cs :
using System.Web.Mvc; namespace WebApplication33.Controllers { public class LibraryController : Controller { // // GET: /Library/ public ActionResult Details(int? id) { return View(); } } }
ビューの用意
Views>Book>Details.cshtml
Views>Library>Details.cshtml
の2つを用意します。どっちも Details.cshtml なのでごっちゃにならないよう注意。
Views>Book>Details.cshtml :
@{ ViewBag.Title = "Details"; } <h2>Book Details</h2> <h4>同じコントローラーへのリンク</h4> <pre> @Html.ActionLink("Go to ~/Book/Details", "Details") @Html.ActionLink("Go to ~/Book/Details/4", "Details", new { id = 4 }) </pre> <h4>違うコントローラーへのリンク</h4> <pre> @Html.ActionLink("Go to ~/Library/Details", "Details", "Library") @Html.ActionLink("間違った例:Go to ~/Library/Details/4", "Details", "Library", new { id = 4 }) @Html.ActionLink("正しい例1:Go to ~/Library/Details/4", "Details", new { id = 4, controller = "Library" }) @Html.ActionLink("正しい例2:Go to ~/Library/Details/4", "Details", "Library", new { id = 4 }, null) </pre>
Views>Library>Details.cshtml :
@{ ViewBag.Title = "Details"; } <h2>Library Index</h2>
以上です。デバッグ実行して「~/Book/Details」にアクセスします。