2008-11-11 50 views
64

我怎样才能返回不同作用的结果或将用户移动到不同的操作,如果在我的ModelState错误,而不会失去我的ModelState信息?如何使用RedirectToAction维护ModelState?

的情况是;删除操作接受来自我的索引操作/视图呈现的DELETE表单的POST。如果删除中存在错误我想将用户移回索引操作/视图并显示ViewData.ModelState中删除操作存储的错误。这怎么能在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"); 
} 

回答

89

存储View数据的TempData,并从那里你指数行动找回它,如果它存在。

... 
    if (!ModelState.IsValid) 
     TempData["ViewData"] = ViewData; 

    RedirectToAction("Index"); 
} 

public ActionResult Index() 
{ 
    if (TempData["ViewData"] != null) 
    { 
     ViewData = (ViewDataDictionary)TempData["ViewData"]; 
    } 

    ... 
} 

[编辑]我检查了上线源为MVC和看来该ViewData的在控制器是可设置的,所以它可能是最简单的只是转移的ViewData的所有,包括的ModelState,到指数行动。

+0

ViewData.ModelState没有setter。 – 2008-11-11 01:34:00

-2

也许尝试的

return View("Index"); 

代替

return Index(); 
+0

这不起作用,因为它不执行索引操作中的逻辑。它所做的只是尝试使用索引视图呈现当前模型。 – 2008-11-11 00:42:22

+0

难道你只是想在你发布的相同视图上显示模型错误吗?在有模型错误时需要执行的Index操作中,你在做什么?当出现错误并且工作正常时,我只返回View(“ViewName”,model)。 – 2008-11-11 01:43:30

+0

不,我想重定向到Index动作,将视图绑定到由该动作生成的数据以及由失败的Delete操作定义的ModelState。 – 2008-11-11 04:45:30

10

请注意,tvanfosson的解决方案并不总是可行的,但在大多数情况下,应该就好。

与特定解决方案的问题是,如果你已经有任何ViewData的或的ModelState你结束了先前的请求的状态,改写了这一切。例如,新请求可能会有一些与传递给动作的无效参数相关的模型状态错误,但这些错误最终会因为被覆盖而被隐藏。

另一种情况如预期可能无法正常工作是,如果你有这样的初始化一些ViewData的或的ModelState错误的行为过滤器。再次,它们将被该代码覆盖。

我们正在寻找在ASP.NET MVC的一些解决方案,它会让你更容易地从两个请求合并状态,敬请关注这一点。

感谢, Eilon

37

使用操作过滤器(PRG模式)(很简单,使用属性)

提到herehere

5

在情况下,这是有用的任何人使用我使用PRG @bob的推荐的解决方案:

见第13项 - >link

我传递的VeiwBag来查看消息的增发被写入,并检查/做RedirectToAction("Action")时手动加载的TempData在控制器动作。为了简化(也使其可维护),我稍微扩展了这种方法来检查和存储/加载其他数据。我的操作方法看起来是这样的:

[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); 
} 

在操作过滤器的代码:

// 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); 
    } 
} 

我知道我的变化在这里是什么正在用ModelState中的代码@由@bob提供的链接做了一个很明显的延伸 - 但我不得不在此线程绊倒之前,我甚至想到的处理它以这种方式。