ASP.NET MVC – 如何保留RedirectToAction的ModelState错误?
我有以下两个操作方法(简化问题):
[HttpGet] public ActionResult Create(string uniqueUri) { // get some stuff based on uniqueuri, set in ViewData. return View(); } [HttpPost] public ActionResult Create(Review review) { // validate review if (validatedOk) { return RedirectToAction("Details", new { postId = review.PostId}); } else { ModelState.AddModelError("ReviewErrors", "some error occured"); return RedirectToAction("Create", new { uniqueUri = Request.RequestContext.RouteData.Values["uniqueUri"]}); } }
所以,如果validation通过,我redirect到另一个页面(确认)。
如果发生错误,我需要显示与错误相同的页面。
如果我return View()
,则会显示错误,但是如果return RedirectToAction
(如上所示),则会丢失模型错误。
我对这个问题并不感到惊讶,只是想知道你们是如何处理这个问题的?
我当然可以只是返回相同的视图,而不是redirect,但我有填充视图数据,我不得不重复的“创build”方法的逻辑。
有什么build议么?
您需要在您的HttpGet
操作上拥有相同的Review
实例。 要做到这一点,你应该保存一个对象在你的HttpPost
动作的临时variablesReview review
,然后恢复HttpGet
行动。
[HttpGet] public ActionResult Create(string uniqueUri) { //Restore Review review = TempData["Review"] as Review; // get some stuff based on uniqueuri, set in ViewData. return View(review); } [HttpPost] public ActionResult Create(Review review) { //Save you object TempData["Review"] = review; // validate review if (validatedOk) { return RedirectToAction("Details", new { postId = review.PostId}); } else { ModelState.AddModelError("ReviewErrors", "some error occured"); return RedirectToAction("Create", new { uniqueUri = Request.RequestContext.RouteData.Values["uniqueUri"]}); } }
另外我会build议,如果你想让它工作,当浏览器刷新button后执行第一次HttpGet
行动,你可能会像这样
Review review = TempData["Review"] as Review; TempData["Review"] = review;
否则,刷新button对象review
将是空的,因为TempData["Review"]
没有任何数据。
我今天不得不解决这个问题,并且遇到了这个问题。
一些答案是有用的(使用TempData),但是并没有真正回答这个问题。
我发现的最好的build议是在这篇博文中:
http://www.jefclaes.be/2012/06/persisting-model-state-when-using-prg.html
基本上,使用TempData来保存和恢复ModelState对象。 但是,如果将其抽象为属性,则会更清晰。
例如
public class SetTempDataModelStateAttribute : ActionFilterAttribute { public override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); filterContext.Controller.TempData["ModelState"] = filterContext.Controller.ViewData.ModelState; } } public class RestoreModelStateFromTempDataAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { base.OnActionExecuting(filterContext); if (filterContext.Controller.TempData.ContainsKey("ModelState")) { filterContext.Controller.ViewData.ModelState.Merge( (ModelStateDictionary)filterContext.Controller.TempData["ModelState"]); } } }
然后按照你的例子,你可以像这样保存/恢复ModelState:
[HttpGet] [RestoreModelStateFromTempData] public ActionResult Create(string uniqueUri) { // get some stuff based on uniqueuri, set in ViewData. return View(); } [HttpPost] [SetTempDataModelState] public ActionResult Create(Review review) { // validate review if (validatedOk) { return RedirectToAction("Details", new { postId = review.PostId}); } else { ModelState.AddModelError("ReviewErrors", "some error occured"); return RedirectToAction("Create", new { uniqueUri = Request.RequestContext.RouteData.Values["uniqueUri"]}); } }
如果你还想在TempData中传递模型(如bigbbuild议),那么你仍然可以这样做。
为什么不用“Create”方法中的逻辑创build一个私有函数,并从Get和Post方法中调用这个方法,并返回View()。
我build议你返回视图,并通过动作的一个属性避免重复。 这是一个填充查看数据的例子。 你可以用你的创build方法逻辑做类似的事情。
public class GetStuffBasedOnUniqueUriAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { var filter = new GetStuffBasedOnUniqueUriFilter(); filter.OnActionExecuting(filterContext); } } public class GetStuffBasedOnUniqueUriFilter : IActionFilter { #region IActionFilter Members public void OnActionExecuted(ActionExecutedContext filterContext) { } public void OnActionExecuting(ActionExecutingContext filterContext) { filterContext.Controller.ViewData["somekey"] = filterContext.RouteData.Values["uniqueUri"]; } #endregion }
对于那些刚刚很难想象如何工作的人来说,
[HttpGet, GetStuffBasedOnUniqueUri] public ActionResult Create() { return View(); } [HttpPost, GetStuffBasedOnUniqueUri] public ActionResult Create(Review review) { // validate review if (validatedOk) { return RedirectToAction("Details", new { postId = review.PostId }); } ModelState.AddModelError("ReviewErrors", "some error occured"); return View(review); }
我可以使用TempData["Errors"]
TempData通过保存数据1次的操作传递。
我有一种将模型状态添加到临时数据的方法。 然后我有一个方法在我的基地控制器检查任何错误的临时数据。 如果有,则将其添加回模型状态。
我的场景有点复杂,因为我使用的是PRG模式,所以我的ViewModel(“SummaryVM”)在TempData中,我的摘要屏幕显示它。 这个页面上有一个小表单,用于向另一个Action发送一些信息。 复杂性来源于用户需要编辑此页面的SummaryVM中的一些字段。
Summary.cshtml具有validation摘要,它将捕获我们将创build的ModelState错误。
@Html.ValidationSummary()
我的表单现在需要发布到Summary()的HttpPost操作。 我有另一个非常小的视图模型来表示编辑的领域,和modelbinding会得到这些给我。
新的forms:
@using (Html.BeginForm("Summary", "MyController", FormMethod.Post)) { @Html.Hidden("TelNo") @* // Javascript to update this *@
和行动…
[HttpPost] public ActionResult Summary(EditedItemsVM vm)
在这里我做了一些validation,我发现一些不好的input,所以我需要返回到错误的摘要页面。 为此我使用TempData,它将在redirect中生存下来。 如果数据没有问题,我将SummaryVM对象replace为一个副本(但当然编辑的字段已更改),然后执行RedirectToAction(“NextAction”);
// Telephone number wasn't in the right format List<string> listOfErrors = new List<string>(); listOfErrors.Add("Telephone Number was not in the correct format. Value supplied was: " + vm.TelNo); TempData["SummaryEditedErrors"] = listOfErrors; return RedirectToAction("Summary");
摘要控制器操作(在这一切开始的地方)查找tempdata中的任何错误并将它们添加到模型状态。
[HttpGet] [OutputCache(Duration = 0)] public ActionResult Summary() { // setup, including retrieval of the viewmodel from TempData... // And finally if we are coming back to this after a failed attempt to edit some of the fields on the page, // load the errors stored from TempData. List<string> editErrors = new List<string>(); object errData = TempData["SummaryEditedErrors"]; if (errData != null) { editErrors = (List<string>)errData; foreach(string err in editErrors) { // ValidationSummary() will see these ModelState.AddModelError("", err); } }
- C#6.0function不适用于Visual Studio 2015
- “处理您的请求时发生exception。 另外,执行自定义错误页面时发生另一个exception…“
- Html.Hidden和Html.HiddenFor有什么区别?
- MVC3 Url.Action查询string生成
- 如何使用dependency injection的属性?
- ASP.NET MVC中EditorFor()的Html属性
- 使用Elmah MVC无法访问生产服务器上的/ elmah?
- 如何使用ASP.NET MVC 3和Stackoverflow的Markdown
- 使用JavaScript / jQueryredirect到ASP.NET MVC中的另一个页面