miso_soup3 Blog

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

ASP.NET MVC 違うコントローラーへのリンクを貼る

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

f:id:miso_soup3:20131217052158p:plain

今回は、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 を確かめることができます。

f:id:miso_soup3:20131217050506p:plain

Razor はこちら↓

f:id:miso_soup3:20131217050602p:plain

プロジェクト用意

準備

プロジェクトの用意

Visual Studio のサイト から、Visual Studio Express 2013 for Web(以下、VS Express 2013)をインストールします。無償です。

VS Express 2013 を開き、メニューから ファイル>新規作成>プロジェクト を選択します。
「ASP.NET Web アプリケーション」を選択し、OK します。

「MVC」を選択し 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」にアクセスします。