WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

EditorFor と DisplayFor の違い

by WebSurfer 27. March 2013 21:38

Html Helper の EditorFor と DisplayFor が、POST 要求に対する応答で、異なった値を表示することがあります。その理由と解決策を備忘録として書いておきます。

EditFor と DisplayFor の違い

上の画像を表示した Model, View, Controller のコードは以下の通りです(説明に関係ない部分は省略してあります)。

Model

public class FileModel
{
  public string FileName { get; set; }
}

View

@model MvcApplication1.Models.FileModel

@using (Html.BeginForm())
{
  @Html.EditorFor(m => m.FileName)
  <input type="submit" value="POST" />
  @Html.DisplayFor(m => m.FileName)
}

Controller

public class FileController : Controller
{
  [HttpPost]
  public ActionResult Index(FileModel m)
  {
    m.FileName += ".ext";
    return View(m);
  }
}

ユーザーがテキストボックス(EditorFor 相当)にファイル名を入力し、form を submit(POST 要求)すると、Controller で model の FileName プロパティに拡張子 ".ext" を追加します。

この場合、応答画面では、DisplayFor には拡張子 ".ext" が追加されて表示されるものの、EditorFor には ".ext" は追加されません。 上の画像の例を見てください。

つまり、EditorFor には POST された値がそのまま使われています。

何故そうなるかと言うと、例えば、EditorFor の入力にユーザーが間違った値を入力した場合、サーバーに POST された時に検証 NG とし、エラーメッセージ(例:入力が間違っています)を表示するとともに、EditorFor にはユーザー入力をそのまま表示したいという理由だそうです。

具体的には、POST された値は ModelState ディクショナリ(model ではない)に格納されていて、Html Helper はまず ModelState ディクショナリを調べて、そこに値があればそれを表示するようになっています。

詳しくは以下のページを見てください。

ASP.NET MVC’s Html Helpers Render the Wrong Value!

value が POST されるか否かが問題になります。例えば、EditorFor に限らず、HiddenFor もその value は POST されるので、結果は EditorFor と同じになります。

一方、DisplayFor には、POST される value などというものはないので、model の値が使われます。

EditorFor にも DisplayFor と同様に model の値(拡張子 .ext が追加されたもの)を表示するには、ModelState ディクショナリを Clear することです。

そうすれば model から値を取ってくるので、期待した結果になります。具体的には以下のコードを追加します。

if (ModelState.IsValid)
{
  ModelState.Clear();
}

ただしこのようにして、検証結果 OK で POST 要求への応答をそのまま返すのは、二重 POST の問題が起こりうるので、好ましくないようです。

二重 POST 問題

MVC に限らず、Web アプリケーション開発の基本として、Post/Redirect/Get (PRG) パターンを使う・・・即ち、POST 要求への応答をそのまま返すのは検証結果 NG の場合のみとし、検証結果 OK の場合は、例え同じページを表示するにしても、リダイレクトしてブラウザに GET 要求させるのがよいそうです。(詳しくはリンク先を見てください)

Post/Redirect/Get による問題解決

Post/Redirect/Get パターンを使うように、上記のコードを書き直すと、以下のようになると思います。

public class FileController : Controller
{
  [HttpGet]
  public ActionResult Index()
  {
    FileModel model;

    object obj = TempData["ValidationResult"];
    if (obj is FileModel)
    {
      // 検証 OK ⇒ 再確認
      model = (FileModel)obj;
    }
    else
    {
      // 初期画面
      model = new FileModel();
    }
    return View(model);
  }

  [HttpPost]
  public ActionResult Index(FileModel m)
  {
    if (ModelState.IsValid)
    {
      // 検証 OK ⇒ 再確認
      m.FileName =
        ModelState["FileName"].Value.AttemptedValue
        + ".ext";
      TempData["ValidationResult"] = m;
      return RedirectToAction("Index");
    }
    else
    {
      // 検証失敗
      return View(m);
    }
  }
}

Tags:

MVC

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  June 2021  >>
MoTuWeThFrSaSu
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar