如果我的ModelState中存在错误而不丢失我的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");
}
答案 0 :(得分:94)
将您的视图数据存储在TempData中,并在Index操作中从那里检索它(如果存在)。
...
if (!ModelState.IsValid)
TempData["ViewData"] = ViewData;
RedirectToAction( "Index" );
}
public ActionResult Index()
{
if (TempData["ViewData"] != null)
{
ViewData = (ViewDataDictionary)TempData["ViewData"];
}
...
}
[编辑]我检查了MVC的在线源代码,看起来Controller中的ViewData是可设置的,因此最简单的方法就是将所有ViewData(包括ModelState)传输到Index操作。 / p>
答案 1 :(得分:40)
答案 2 :(得分:10)
请注意,tvanfosson的解决方案并不总是有效,但在大多数情况下应该没问题。
该特定解决方案的问题在于,如果您已经拥有任何ViewData或ModelState,则最终会使用先前请求的状态覆盖所有ViewData或ModelState。例如,新请求可能有一些与传递给操作的无效参数相关的模型状态错误,但这些错误最终会被覆盖,因为它们会被覆盖。
如果您有一个初始化某些ViewData或ModelState错误的Action Filter,那么它可能无法正常工作的另一种情况。同样,它们将被该代码覆盖。
我们正在研究ASP.NET MVC的一些解决方案,它们可以让您更轻松地合并两个请求的状态,敬请期待。
谢谢, Eilon
答案 3 :(得分:5)
如果这对任何使用PRB的@bob推荐解决方案的人都有用:
见第13项 - > link
在执行RedirectToAction("Action")
时,我在VeiwBag中传递了另外一个消息,即在控制器操作中从TempData手动编写和检查/加载View。为了简化(并使其可维护),我稍微扩展了这种方法来检查和存储/加载其他数据。我的行动方法看起来像:
[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);
}
}
我意识到我的更改是对@Bob提供的代码@的链接已经完成的ModelState的一个非常明显的扩展 - 但我不得不偶然发现这个线程,我甚至想到在此处理它方式。
答案 4 :(得分:0)
请不要让我为这个答案而措手不及。这是一个合理的建议。
使用AJAX
用于管理ModelState的代码很复杂,并且(可能是?)指示代码中的其他问题。
您可以轻松滚动自己的AJAX javascript代码。这是我使用的脚本:
https://gist.github.com/jesslilly/5f646ef29367ad2b0228e1fa76d6bdcc#file-ajaxform
(function ($) {
$(function () {
// For forms marked with data-ajax="#container",
// on submit,
// post the form data via AJAX
// and if #container is specified, replace the #container with the response.
var postAjaxForm = function (event) {
event.preventDefault(); // Prevent the actual submit of the form.
var $this = $(this);
var containerId = $this.attr("data-ajax");
var $container = $(containerId);
var url = $this.attr('action');
console.log("Post ajax form to " + url + " and replace html in " + containerId);
$.ajax({
type: "POST",
url: url,
data: $this.serialize()
})
.done(function (result) {
if ($container) {
$container.html(result);
// re-apply this event since it would have been lost by the form getting recreated above.
var $newForm = $container.find("[data-ajax]");
$newForm.submit(postAjaxForm);
$newForm.trigger("data-ajax-done");
}
})
.fail(function (error) {
alert(error);
});
};
$("[data-ajax]").submit(postAjaxForm);
});
})(jQuery);
答案 5 :(得分:-2)
也许试试
return View("Index");
而不是
return Index();