前にも、この方法について考えたことがあって、その時はストラテジパターンを使ってDelegateでのコマンド振り分けでの実装をしてたんですが、少し前に違う方法を実装してるのを見て悔い改めたんです。
MVCによく似合う方法は、属性ベースで対象となるアクションを振り分ける(判定する)方法ですよね。
AcceptVerbsでHTTP Method毎に処理を振り分ける事ができるのを上手く利用して、Submitの値毎に処理を振り分けるためにActionMethodSelectorAttributeを派生したSubmitCommandAttributeというのを定義していました。
using System;
using System.Reflection;
using System.Web.Mvc;
namespace MvcApplication2.Controllers
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class SubmitCommandAttribute : ActionMethodSelectorAttribute
{
private string _submitName;
private string _submitValue;
private static readonly AcceptVerbsAttribute _innerAttribute =
new AcceptVerbsAttribute(HttpVerbs.Post);
public SubmitCommandAttribute(string name) : this(name, string.Empty) { }
public SubmitCommandAttribute(string name, string value)
{
_submitName = name;
_submitValue = value;
}
public override bool IsValidForRequest(ControllerContext controllerContext,
MethodInfo methodInfo)
{
if (!_innerAttribute.IsValidForRequest(controllerContext, methodInfo))
return false;
// Form Value
var submitted = controllerContext.RequestContext
.HttpContext
.Request.Form[_submitName];
return string.IsNullOrEmpty(_submitValue)
? !string.IsNullOrEmpty(submitted)
: string.Equals(submitted, _submitValue,
StringComparison.InvariantCultureIgnoreCase);
}
}
}
これだけなんですけども...。HttpPostのコードを参考に内部にAcceptVerbsAttributeを保持してPOST時のみをIsValidにしてますが、DeleteとPutも必要なら要修正。
using System.Web.Mvc;
namespace MvcApplication2.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
[ActionName("Different")]
[SubmitCommand("DoSave")]
public ActionResult DifferentSave()
{
TempData["message"] = "saved! - defferent";
return View("Index");
}
[ActionName("Different")]
[SubmitCommand("DoDelete")]
public ActionResult DifferentDelete()
{
TempData["message"] = "deleted! - defferent";
return View("Index");
}
[ActionName("Same")]
[SubmitCommand("DoSubmit","保存")]
public ActionResult SameSave()
{
TempData["message"] = "saved! - same";
return View("Index");
}
[ActionName("Same")]
[SubmitCommand("DoSubmit","削除")]
public ActionResult SameDelete()
{
TempData["message"] = "deleted! - same";
return View("Index");
}
}
}
コントローラでこんな感じにアクションを定義しておいて、Viewを以下のようにしておきます。
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Index</title> </head> <body> <h1><%= TempData["message"] ?? "Click some button" %></h1> <h2>異なるnameのsubmit</h2> <% using (Html.BeginForm("Different", "Home")) { %> <input type="submit" name="DoSave" value="保存" /><br /> <input type="submit" name="DoDelete" value="削除" /> <% } %> <h2>同一nameでValueの異なるsubmit</h2> <% using (Html.BeginForm("Same","Home")) { %> <input type="submit" name="DoSubmit" value="保存" /><br /> <input type="submit" name="DoSubmit" value="削除" /> <% } %> </body> </html>
動かしてみると、最初に↓。ボタンが4種類出てきます。上2つはnameとvalueの異なるsubmitで、下2つがnameが同じでvalueが違うsubmit。
上から順にボタンを押したのが↓。
ちゃんとTempDataの値が違うのが確認できますね(ページ上部のH1)。
で、ココまで書いときながら、前にどこで見たのかを検索して探し出してみてショック。
ASP.NET MVC – Multiple buttons in the same form - David Findley's Blog
まんま、同じになるという大失態。やっぱり年始からこんなコードを書いてて先が思いやられる...。