ASP.NET MVC 4(六)ヘルプ関数

56118 ワード

ヘルプ関数はいくつかのコードをカプセル化して、私たちがアプリケーションの中で再利用するのに便利で、MVC内に多くのヘルプ関数を建てて、HTMLタグを簡単に生成することができます.まず、次の例で使用するデータモデルクラス定義をリストします.
namespace HelperMethods.Models {

    public partial class Person {
        public int PersonId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime BirthDate { get; set; }
        public Address HomeAddress { get; set; }
        public bool IsApproved { get; set; }
        public Role Role { get; set; }
    }

    public class Address {
        public string Line1 { get; set; }
        public string Line2 { get; set; }
        public string City { get; set; }
        [Display(Name = "ZIP CODE")]
        public string PostalCode { get; set; }
        public string Country { get; set; }
    }

    public enum Role {
        Admin,
        User,
        Guest
    }
}

コントローラの定義:
namespace HelperMethods.Controllers {
    public class HomeController : Controller {

        public ActionResult Index() {

            ViewBag.Fruits = new string[] { "Apple", "Orange", "Pear" };
            ViewBag.Cities = new string[] { "New York", "London", "Paris" };

            string message = "This is an HTML element: <input>";

            return View((object)message);
        }

        public ActionResult CreatePerson() {
            return View(new Person { Role = Role.Guest});
        }

        [HttpPost]
        public ActionResult CreatePerson(Person person) {
            return View("DisplayPerson", person);
        }
    }
}

 
インラインヘルプ関数
@helperタグを使用して、ビュー内のヘルプ関数を直接定義できます.
@model string
@{
    Layout = null;
}
@helper ListArrayItems(string[] items)
{
    foreach (string str in items)
    {
        <b>@str </b>
    }
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div>
        Here are the fruits: @ListArrayItems(ViewBag.Fruits)
    </div>
    <div>
        Here are the cities: @ListArrayItems(ViewBag.Cities)
    </div>
    <div>
        Here is the message:
        <p>@Model</p>
    </div>
</body>
</html> 

ここではインラインヘルプ関数ListArrayItemsを定義し、その後、数値内容を表示するために再利用できます.インラインヘルプ関数には戻り値がありません.ListArrayItemsを呼び出すときもパラメータにタイプCastを付けず、MVCは自動的にタイプ変換を実現します.
外部ヘルプ関数
インラインヘルプ関数は便利ですが、ビューでしか定義できません.インラインヘルプ関数が複雑であると、ビューが読みにくくなります.これに対して、外部ヘルプ関数を定義できます.外部ヘルプ関数は実際にはHtmlHelperクラスのメソッドに拡張されています.上のインラインヘルプ関数を外部ヘルプ関数に書き換えると、次のようになります.
namespace HelperMethods.Infrastructure {
    public static class CustomHelpers {

        public static MvcHtmlString ListArrayItems(this HtmlHelper html, string[] list) {

            TagBuilder tag = new TagBuilder("ul");
            foreach(string str in list) {
                TagBuilder itemTag = new TagBuilder("li");
                itemTag.SetInnerText(str);
                tag.InnerHtml += itemTag.ToString();
            }

            return new MvcHtmlString(tag.ToString());
        }

    }
}

HtmlHelperは、RouteCollection、View Bag、View Contextなどの属性を暴露し、現在のリクエストに関連するデータを取得するのに便利です.外部ヘルプ関数は、出力応答に直接書き込まれるMvcHtmlStringオブジェクトを最後に返します.このようにして、ビューでカスタム外部ヘルプ関数を使用できます.
@model string
@using HelperMethods.Infrastructure
@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div>
        Here are the fruits: @Html.ListArrayItems((string[])ViewBag.Fruits) </div>
    <div>
        Here are the cities: @Html.ListArrayItems((string[])ViewBag.Cities) </div>
    <div>
        Here is the message:
        <p>@Model</p>
    </div>
</body>
</html> 

外部ヘルプ関数を定義するネーミングスペースを導入する必要があります./Views/Webで定義することもできます.configでは、すべてのビューで使用できます.@htmlを使用して外部ヘルプ関数を呼び出します.HtmlHelperクラスのインスタンスを返します.外部ヘルプ関数を呼び出すときもパラメータタイプ変換を行います.
ヘルプ関数の文字列エンコーディング
ヘルプ関数を使用する場合は、HTML符号化の問題に注意する必要があります.まず、コントローラactionでHTMLタグ付き文字列をビューに出力する場合は、次の問題を見てみましょう.
public ActionResult Index() { 
  string message = "This is an HTML element: <input>"; 
  return View((object)message); 
} 

この文字列をビューに直接出力します.
@model string

@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <title>Index</title>
</head>
<body>
    <p>This is the content from the view:</p>
    <div>
        Here is the message:
        <p>@Model</p>
    </div>
</body>
</html>

最終Razor出力の結果は次のとおりです.
... 
<div> 
Here is the message: 
<p>This is an HTML element: &lt;input&gt;</p> 
</div> 
... 

の「<>」タブは、HTMLタグではなく対応する文字として符号化されています.これは、安全上の理由から、HTMLタグやJavascriptスクリプトの動的埋め込みを防止します.ヘルプ関数からHTMLタグを出力するとしたらどのような結果になるのでしょうか?次の例を見てください.
public static MvcHtmlString DisplayMessage(this HtmlHelper html, string msg) { 
    string result = String.Format("This is the message: <p>{0}</p>", msg); 
  return new MvcHtmlString(result); 
}

ビューファイルでこのヘルプ関数を呼び出してHTMLタグを出力します.
<p>This is the content from the helper method:</p> 
<div style="border: thin solid black; padding: 10px"> 
@Html.DisplayMessage("This is an HTML element: <input>") 
</div> 

今回得られたのがHTMLの編集ボックスです.これは、私たちのHTMLヘルプ関数が返すMvcHtmlStringが安全と信頼され、出力の結果をHTML符号化しないためです.ヘルプ関数がMvcHtmlStringではなく、通常のStringを返します.たとえば、次のようになります.
public staticstring DisplayMessage(this HtmlHelper html, string msg) { 
  return String.Format("This is the message: <p>{0}</p>","This is an HTML element: <input>"); } 

このとき文字列はHTMLで符号化され、inputタグは出力されません.しかし、このような結果は

タグもHTMLで符号化され、これは私たちが望んでいる結果ではなく、正しい方法は:

public static MvcHtmlStringDisplayMessage(this HtmlHelper html) { 
  string encodedMessage = html.Encode("This is an HTML element: <input>"); 
  string result = String.Format("This is the message: <p>{0}</p>", encodedMessage); 
  return new MvcHtmlString(result); 
} 

htmlを使います.Encode()はダイナミックコンテンツを符号化し、結果としてMvcHtmlStringオブジェクトを返します.これにより、ダイナミックコンテンツの埋め込みによるセキュリティ・ホールが回避され、本当に表示するHTMLタグが正しくレンダリングされます.
組み込まれたFormヘルプ関数
HTMLのFormタグを使用して、コミットするデータを編集するフォームを直接作成できます.
@model HelperMethods.Models.Person
@{
    ViewBag.Title = "CreatePerson";
}
<h2>CreatePerson</h2>
<form action="/Home/CreatePerson" method="post">
    <div class="dataElem">
        <label>PersonId</label>
        <input name="personId" value="@Model.PersonId" />
    </div>
    <div class="dataElem">
        <label>First Name</label>
        <input name="FirstName" value="@Model.FirstName" />
    </div>
    <div class="dataElem">
        <label>Last Name</label>
        <input name="lastName" value="@Model.LastName" />
    </div>
    <input type="submit" value="Submit" />
</form> 

このような問題は、フォームがコミットしたURLをハードコーディングする必要があることです.実際には、組み込まれたBeginFormヘルプ関数を使用してフォームを作成することができます.
@Html.BeginForm()
<div class="dataElem">
    <label>PersonId</label>
    <input name="personId" value="@Model.PersonId" />
</div>
<div class="dataElem">
    <label>First Name</label>
    <input name="FirstName" value="@Model.FirstName" />
</div>
<div class="dataElem">
    <label>Last Name</label>
    <input name="lastName" value="@Model.LastName" />
</div>
<input type="submit" value="Submit" />
@{Html.EndForm();} 

BeginFormはフォームを開始し、EndFormはフォームを終了します.実際にはRazorコードブロックを使用するのが一般的です.
@using (Html.BeginForm())
{
    <div class="dataElem">
        <label>PersonId</label>
        <input name="personId" value="@Model.PersonId" />
    </div>
    <div class="dataElem">
        <label>First Name</label>
        <input name="FirstName" value="@Model.FirstName" />
    </div>
    <div class="dataElem">
        <label>Last Name</label>
        <input name="lastName" value="@Model.LastName" />
    </div>
    <input type="submit" value="Submit" />
} 

@usingは、EndFormを呼び出す必要がなくなり、自己閉じたフォームを作成します.パラメータなしのBeginFormは、現在のコントローラの現在のactionメソッドにフォームをコミットします.このほか、BeginFormには多くのリロード形式があります.次に、使用例を示します.
@using (Html.BeginForm("CreatePerson", "Home",new { id = "MyIdValue" }, FormMethod.Post,new { @class = "personClass", data_formType="person"})) { 
...
}

ここでCreatePersonはフォームをコミットするコントローラを指定し、Homeはコミットするaction、new{id="MyIdValue"}は追加のパスマッピングパラメータ、FormMethod.Post指定HTTP postメソッドを使用してデータをコミットし、new{@class=“personClass”、data_formType=“person”}でFormタグの属性を指定すると、フォームHTMLの結果は次のようになります.
... 
<form action="/Home/CreatePerson/MyIdValue" class="personClass" data-formType="person" 
method="post"> 
... 

また、フォームで使用するパスマッピングを指定して、コミットされたリンクを生成することもできます.たとえば、このようなパスマッピングを登録しました.
routes.MapRoute( name: "FormRoute", url: "app/forms/{controller}/{action}" ); 

BeginRouteFormを使用してフォームを作成し、使用するパスマッピングレコードを指定します.
@using(Html.BeginRouteForm("FormRoute", new {}, FormMethod.Post, new { @class = "personClass", data_formType="person"})) { 
...
}

最後に得られた結果は、
... 
<form action="/app/forms/Home/CreatePerson"class="personClass" data-formType="person" method="post"> 
... 

組み込み入力ヘルプ関数
実際にはなどのHTMLタグを使用してデータの編集ボックスを作成する必要はありません.MVC組み込みでは、多くの入力ヘルプ関数が提供されています.
HTML要素

出力結果
Checkbox
Html.CheckBox("myCheckbox", false)

Hidden
Html.Hidden("myHidden", "val")
 
Radio
Html.RadioButton("myRadiobutton", "val", true) 

Password
Html.Password("myPassword", "val")
Html.Password("myPassword", "val")
Text area
Html.TextArea("myTextarea", "val", 5, 20, null)