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

miso_soup3 Blog

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

ASP.NET MVC TempData を使ってリダイレクト後にメッセージを表示する

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

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

今回は、TempData についてです。

TempData とは

ASP.NET MVC 用の機能です。ASP.NET Web Forms には用意されておりません。

ページ間で値を共有するための仕組みの1つで、前回(ASP.NET MVC セッション状態の値を操作してみる)で紹介した「セッション状態」を利用した機能です。ですが、同じ「セッション状態」を利用した機能でも、この TempData は一時的、または一度しか使わないような値を扱うために用意された機能です。

よくある利用としては、リダイレクト後にメッセージを表示する、があります。
今回はこの例をもとにサンプルを実装してみます。

参考:

サンプル概要

Person データの一覧ページと、新規追加ページを用意します。
新規追加ページで登録した後は、一覧ページにリダイレクトし、その一覧ページにて「【名前:太郎】 の登録が完了しました!」という案内メッセージを表示させます。
この案内メッセージは、登録後のリダイレクト後でしか表示されません。リンクをクリックする等の通常のアクセスでは、表示されません。

概要図は下の図の通りです。

f:id:miso_soup3:20131214052746p:plain

サーバー側で登録する際に、TempData に案内メッセージをセットします。そして、一覧ページを表示する際に TempData から値を取得し表示します。

実装

プロジェクトの用意

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

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

「MVC」を選択し OK します。

モデル(クラス)を用意

入力フォームのモデルを用意します。クラス「CreatePersonModel.cs」を作成します。

CreatePersonModel.cs :

using System.ComponentModel.DataAnnotations;

namespace WebApplication6.Models
{
	public class CreatePersonModel
	{
		[Required]
		[Display(Name = "名前")]
		public string Name { get; set; }
	}
}
コントローラーの用意

次のアクションを提供するために、PersonController クラスを用意します。

  • GET ~/Person/List : Person データを一覧表示。TempData から案内メッセージを取得する。
  • GET ~/Person/Create : 新規作成ページの表示。
  • POST ~/Person/Create : Person データを登録する。TempData に案内メッセージをセットする。

今回は DB へのアクセスは行ってません。

PersonController.cs :

using System;
using System.Web.Mvc;
using WebApplication6.Models;

namespace WebApplication6.Controllers
{
	public class PersonController : Controller
	{
		//一覧ページ
		[HttpGet]
		public ActionResult List()
		{
			//実際は、ここで Person データ一覧を取得したりする

			//TempData より操作メッセージを取得する
			ViewBag.OperationMessage = TempData["OperationMessage"];

			return View();
		}

		//新規作成ページ
		[HttpGet]
		public ActionResult Create()
		{
			return View();
		}

		//登録
		[HttpPost]
		public ActionResult Create(CreatePersonModel model)
		{
			//実際は、ここでデータの登録を行ったりする

			//TempData に操作メッセージをセットする
			String message = String.Format("【名前:{0}】 の登録が完了しました!", model.Name);
			TempData["OperationMessage"] = message;

			return RedirectToAction("List");
		}
	}
}

TempData は「セッション状態」と同じく、キーと値のペアで管理されます。
TempData の値を設定するには

TempData["キー"] = 値;

TempData の値を取得するには

TempData["キー"]

で取得できます。

ビューの用意

~/Pesron/List と ~/Person/Create の2つのビューを用意します。

List.cshtml :

@{ ViewBag.Title = "List"; }

<h2>一覧ページ</h2>

<div>
	<p>@ViewBag.OperationMessage</p>
</div>

<ul>
	<li>ダミーデータ1</li>
	<li>ダミーデータ2</li>
</ul>

<div>
	@Html.ActionLink("新規作成ページへ", "Create")
</div>

Create.cshtml :

@model WebApplication6.Models.CreatePersonModel

@{ ViewBag.Title = "Create"; }

<h2>新規作成</h2>

@using (Html.BeginForm()) 
{
    <div class="form-horizontal">
        @Html.ValidationSummary(true)

        <div class="form-group">
            @Html.LabelFor(model => model.Name, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.TextBoxFor(model => model.Name, new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.Name)
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("一覧ページ", "List")
</div>

@section Scripts {
	@Scripts.Render("~/bundles/jqueryval")
}

以上で実装は終了です。

確認する

TempData にセットした値が、ページ間で一時的に利用されていることを確認します。

  • 「~/Person/List」 にアクセス
    • → 案内メッセージは表示されない
  • "新規作成ページへ"のリンクをクリックして「~/Person/Create」にアクセスし、登録する。
  • リダイレクトし、一覧ページへ飛ばされる。
    • → 案内メッセージ「【名前:~~】 の登録が完了しました!」が表示されている。
  • F5 を押すなどして、再度一覧ページを表示する。
    • → 案内メッセージは表示されない。

また、セッション状態と同じように、クッキーが発行されていることにも注目です。参考:前回(ASP.NET MVC セッション状態の値を操作してみる)

Q & A

POST 後にリダイレクトせずそのまま一覧ページの HTML を返せば、TempData を使わなくても実現できるのでは?

確かに実現できます。登録時に呼び出される PersonController の Create メソッドを、↓のように書けば実現できます。

//新規作成
[HttpPost]
public ActionResult Create(CreatePersonModel model)
{
	//実際は、ここでデータの登録を行ったりする

	//操作メッセージをビューに渡す
	String message = String.Format("【名前:{0}】 の登録が完了しました!", model.Name);
	ViewBag.OperationMessage = message;

	//実際は、ここで Person 一覧データを取得する
			
	return View("List");
}

が、POST の後に一覧ページの HTML を返すことは、使い勝手が悪いとされており、POST 後はリダイレクトが推奨されています。
参考:第28回 フォーム送信とブラウザ・ボタンと使い勝手(前編)~PRGパターンをご存じですか
必ずではありませんが、POST でデータ登録に成功した後は、リダイレクトさせるのが一般的です。

なぜ TempData の値を ViewBag に入れているのか?

必ずしなければいけない、ということではありません。
今回のサンプルでは、TempData から取得した値を ViewBag に入れて表示させました。

ビューのコ―ド:

<p>@ViewBag.OperationMessage</p>

ですが、ViewBag に入れずとも、ビューにて直接 TempData を参照することは可能です。

<p>@TempData["OperationMessage"]</p>

前者の場合、ビューがモデル(ViewBag や モデルクラス)のみを参照している一方、後者の場合は、ビューは TempData とモデルの2つを参照する状態になります。

ビューが多くのことを参照している状態だと、コードが煩雑になり、変更し難くなる恐れがあります。

ということで、TempData は一度モデルに渡すのがおすすめです。Session の場合も同様に。(絶対ではないです。)