我如何使用RedirectToAction维护ModelState?
如果我的ModelState中存在错误而又不丢失我的ModelState信息,如何返回其他操作的结果或将用户移动到另一个操作?
情况是; 删除操作接受来自我的索引操作/视图呈现的DELETE表单的POST。 如果删除中存在错误我想将用户移回到索引操作/视图中,并在ViewData.ModelState
显示Delete操作存储的错误。 这怎么能在ASP.NET MVC中完成?
[AcceptVerbs(HttpVerbs.Post | HttpVerbs.Delete)] public ActionResult Delete([ModelBinder(typeof(RdfUriBinder))] RdfUri graphUri) { if (!ModelState.IsValid) return Index(); //this needs to be replaced with something that works :) return RedirectToAction("Index"); }
将视图数据存储在TempData中,并在Index操作中从中检索它(如果存在)。
... if (!ModelState.IsValid) TempData["ViewData"] = ViewData; RedirectToAction( "Index" ); } public ActionResult Index() { if (TempData["ViewData"] != null) { ViewData = (ViewDataDictionary)TempData["ViewData"]; } ... }
[编辑]我检查了MVC的在线来源,看起来控制器中的ViewData是可设置的,所以将所有ViewData(包括ModelState)转移到Index操作可能是最简单的。
使用动作filter(PRG模式)(就像使用属性一样简单)
这里和这里提到。
请注意,tvanfosson的解决scheme并不总是工作,虽然在大多数情况下,它应该是好的。
这个特殊的解决scheme的问题是,如果你已经有了任何ViewData或者ModelState,你最终会把它全部用前一个请求的状态覆盖。 例如,新的请求可能会有一些与传递给动作的无效参数相关的模型状态错误,但是这些错误最终会被覆盖掉。
另一种情况下,它可能无法按预期工作,如果您有一个操作filter,初始化一些ViewData或ModelState错误。 再次,他们将被覆盖的代码。
我们正在研究ASP.NET MVC的一些解决scheme,这将允许您更容易地从两个请求中合并状态,敬请关注。
谢谢,Eilon
如果这对我使用@bobbuild议使用PRG的解决scheme的任何人都有用:
见项目13 – > 链接 。
我做了一个额外的问题,在执行RedirectToAction("Action")
时,在VeiwBag中将View传递给正在写入的视图,并从控制器的ActionData中手动检查/加载。 为了简化(也使其可维护),我略微扩展了这种方法来检查和存储/加载其他数据。 我的行动方法看起来像这样:
[AcceptVerbs(HttpVerbs.Post)] [ExportModelStateToTempData] public ActionResult ChangePassword(ProfileViewModel pVM) { bool result = MyChangePasswordCode(pVM.ChangePasswordViewModel); if (result) { ViewBag.Message = "Password change success"; else { ModelState.AddModelError("ChangePassword", "Some password error"); } return RedirectToAction("Index"); }
而我的指数行动:
[ImportModelStateFromTempData] public ActionResult Index() { ProfileViewModel pVM = new ProfileViewModel { //setup } return View(pVM); }
Action Filters中的代码:
// Following best practices as listed here for storing / restoring model data: // http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx#prg public abstract class ModelStateTempDataTransfer : ActionFilterAttribute { protected static readonly string Key = typeof(ModelStateTempDataTransfer).FullName; }
:
public class ExportModelStateToTempData : ModelStateTempDataTransfer { public override void OnActionExecuted(ActionExecutedContext filterContext) { //Only export when ModelState is not valid if (!filterContext.Controller.ViewData.ModelState.IsValid) { //Export if we are redirecting if ((filterContext.Result is RedirectResult) || (filterContext.Result is RedirectToRouteResult)) { filterContext.Controller.TempData[Key] = filterContext.Controller.ViewData.ModelState; } } // Added to pull message from ViewBag if (!string.IsNullOrEmpty(filterContext.Controller.ViewBag.Message)) { filterContext.Controller.TempData["Message"] = filterContext.Controller.ViewBag.Message; } base.OnActionExecuted(filterContext); } }
:
public class ImportModelStateFromTempData : ModelStateTempDataTransfer { public override void OnActionExecuted(ActionExecutedContext filterContext) { ModelStateDictionary modelState = filterContext.Controller.TempData[Key] as ModelStateDictionary; if (modelState != null) { //Only Import if we are viewing if (filterContext.Result is ViewResult) { filterContext.Controller.ViewData.ModelState.Merge(modelState); } else { //Otherwise remove it. filterContext.Controller.TempData.Remove(Key); } } // Restore Viewbag message if (!string.IsNullOrEmpty((string)filterContext.Controller.TempData["Message"])) { filterContext.Controller.ViewBag.Message = filterContext.Controller.TempData["Message"]; } base.OnActionExecuted(filterContext); } }
我意识到,这里的变化是通过@bob提供的代码@ @ @ @ @ @ @ @ @ @ @ @ @ @现在已经对ModelState做了一个非常明显的扩展,但是我还没有想到以这种方式处理这个线程就偶然发现了这个线程。
也许试试
return View("Index");
代替
return Index();