[英]How can I maintain ModelState with RedirectToAction?
How can I return the result of a different action or move the user to a different action if there is an error in my ModelState
without losing my ModelState
information?如果我的ModelState
存在错误,如何在不丢失我的ModelState
信息的情况下返回不同操作的结果或将用户移动到不同的操作?
The scenario is;场景是; Delete
action accepts a POST from a DELETE form rendered by my Index
Action/View. Delete
操作接受来自我的Index
操作/视图呈现的 DELETE 表单的 POST。 If there is an error in the Delete
I want to move the user back to the Index
Action/View and show the errors that are stored by the Delete
action in the ViewData.ModelState
.如果Delete
有错误,我想将用户移回Index
Action/View 并显示由Delete
操作存储在ViewData.ModelState
。 How can this be done in ASP.NET MVC?如何在 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");
}
Store your view data in TempData
and retrieve it from there in your Index
action, if it exists.将您的视图数据存储在TempData
并在您的Index
操作中从那里检索它(如果存在)。
...
if (!ModelState.IsValid)
TempData["ViewData"] = ViewData;
RedirectToAction( "Index" );
}
public ActionResult Index()
{
if (TempData["ViewData"] != null)
{
ViewData = (ViewDataDictionary)TempData["ViewData"];
}
...
}
[EDIT] I checked the on-line source for MVC and it appears that the ViewData
in the Controller is settable, so it is probably easiest just to transfer all of the ViewData
, including the ModelState
, to the Index action. [编辑] 我检查了 MVC 的在线源,似乎控制器中的ViewData
是可设置的,因此将所有ViewData
,包括ModelState
传输到 Index 操作可能是最简单的。
Please note that tvanfosson's solution will not always work, though in most cases it should be just fine.请注意,tvanfosson 的解决方案并不总是有效,但在大多数情况下应该没问题。
The problem with that particular solution is that if you already have any ViewData or ModelState you end up overwriting it all with the previous request's state.该特定解决方案的问题在于,如果您已经拥有任何 ViewData 或 ModelState,您最终会用先前请求的状态覆盖它们。 For example, the new request might have some model state errors related to invalid parameters being passed to the action, but those would end up being hidden because they are overwritten.例如,新请求可能有一些与传递给操作的无效参数相关的模型状态错误,但这些错误最终会被隐藏,因为它们被覆盖了。
Another situation where it might not work as expected is if you had an Action Filter that initialized some ViewData or ModelState errors.它可能无法按预期工作的另一种情况是,如果您有一个 Action Filter 初始化了一些 ViewData 或 ModelState 错误。 Again, they would be overwritten by that code.同样,它们将被该代码覆盖。
We're looking at some solutions for ASP.NET MVC that would allow you to more easily merge the state from the two requests, so stay tuned for that.我们正在研究 ASP.NET MVC 的一些解决方案,它们可以让您更轻松地合并来自两个请求的状态,因此请继续关注。
Thanks, Eilon谢谢,伊隆
In case this is useful to anyone I used @bob 's recommended solution using PRG:如果这对我使用 PRG 使用 @bob 推荐的解决方案的任何人有用:
I had the additional issue of messages being passed in the VeiwBag to the View being written and checked / loaded manually from TempData in the controller actions when doing a RedirectToAction("Action")
.在执行RedirectToAction("Action")
时,我在控制器操作中从 TempData 手动写入和检查/加载了消息在 VeiwBag 中传递到视图的附加问题。 In an attempt to simplify (and also make it maintainable) I slightly extended this approach to check and store/load other data as well.为了简化(并使其可维护),我稍微扩展了这种方法来检查和存储/加载其他数据。 My action methods looked something like:我的操作方法看起来像:
[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");
}
And my Index Action:还有我的索引操作:
[ImportModelStateFromTempData]
public ActionResult Index() {
ProfileViewModel pVM = new ProfileViewModel { //setup }
return View(pVM);
}
The code in the 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);
}
}
I realize my changes here are a pretty obvious extension of what was already being done with the ModelState by the code @ the link provided by @bob - but I had to stumble on this thread before I even thought of handling it in this way.我意识到我在这里的更改是对 ModelState 已经通过代码@@bob 提供的链接所做的事情的一个非常明显的扩展 - 但在我什至想到以这种方式处理它之前,我不得不偶然发现这个线程。
Please don't skewer me for this answer.请不要因为这个答案而质疑我。 It is a legitimate suggestion.这是一个合理的建议。
Use AJAX使用 AJAX
The code for managing ModelState is complicated and (probably?) indicative of other problems in your code.用于管理 ModelState 的代码很复杂,并且(可能?)表明您的代码中存在其他问题。
You can pretty easily roll your own AJAX javascript code.你可以很容易地推出你自己的 AJAX javascript 代码。 Here is a script I use:这是我使用的脚本:
https://gist.github.com/jesslilly/5f646ef29367ad2b0228e1fa76d6bdcc#file-ajaxform 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);
Maybe try也许试试
return View("Index");
instead of代替
return Index();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.