Wie kann ich das Ergebnis einer anderen Aktion zurückgeben oder den Benutzer zu einer anderen Aktion verschieben, wenn in meinem ModelState ein Fehler auftritt, ohne meine ModelState-Informationen zu verlieren?
Das Szenario ist; Die Aktion "Löschen" akzeptiert einen POST aus einem DELETE-Formular, das von meiner Indexaktion / -ansicht gerendert wurde. Wenn beim Löschen ein Fehler auftritt, möchte ich den Benutzer zurück zur Indexaktion / -ansicht verschieben und die Fehler anzeigen, die von der Löschaktion im gespeichert werden ViewData.ModelState
. Wie kann dies in ASP.NET MVC durchgeführt werden?
[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");
}
quelle
Verwenden Sie Aktionsfilter (PRG-Muster) (so einfach wie das Verwenden von Attributen).
Erwähnt hier und hier .
quelle
Bitte beachten Sie, dass die Lösung von tvanfosson nicht immer funktioniert, obwohl sie in den meisten Fällen in Ordnung sein sollte.
Das Problem bei dieser speziellen Lösung besteht darin, dass Sie, wenn Sie bereits über ViewData oder ModelState verfügen, alles mit dem Status der vorherigen Anforderung überschreiben. Beispielsweise kann die neue Anforderung einige Modellstatusfehler aufweisen, die sich auf ungültige Parameter beziehen, die an die Aktion übergeben werden. Diese werden jedoch ausgeblendet, da sie überschrieben werden.
Eine andere Situation, in der es möglicherweise nicht wie erwartet funktioniert, ist, wenn Sie einen Aktionsfilter hatten, der einige ViewData- oder ModelState-Fehler initialisiert hat. Wieder würden sie durch diesen Code überschrieben.
Wir suchen nach Lösungen für ASP.NET MVC, mit denen Sie den Status aus den beiden Anforderungen einfacher zusammenführen können. Bleiben Sie also auf dem Laufenden.
Danke, Eilon
quelle
Merge
Funktion.Falls dies für jemanden nützlich ist, habe ich die von @bob empfohlene Lösung mit PRG verwendet:
Ich hatte das zusätzliche Problem, dass Nachrichten in der VeiwBag an die Ansicht übergeben und manuell aus TempData in den Controller-Aktionen überprüft / geladen wurden, wenn a ausgeführt wurde
RedirectToAction("Action")
. Um zu vereinfachen (und auch wartbar zu machen), habe ich diesen Ansatz leicht erweitert, um auch andere Daten zu überprüfen und zu speichern / laden. Meine Handlungsmethoden sahen ungefähr so aus:[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"); }
Und meine Indexaktion:
[ImportModelStateFromTempData] public ActionResult Index() { ProfileViewModel pVM = new ProfileViewModel { //setup } return View(pVM); }
Der Code in den Aktionsfiltern:
// 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); } }
Mir ist klar, dass meine Änderungen hier eine ziemlich offensichtliche Erweiterung dessen sind, was bereits mit dem ModelState durch den Code @ the link von @bob gemacht wurde - aber ich musste über diesen Thread stolpern, bevor ich überhaupt daran dachte, ihn auf diese Weise zu handhaben.
quelle
Bitte spießen Sie mich nicht für diese Antwort auf. Es ist ein legitimer Vorschlag.
Verwenden Sie AJAX
Der Code zum Verwalten von ModelState ist kompliziert und weist (wahrscheinlich?) Auf andere Probleme in Ihrem Code hin.
Sie können ganz einfach Ihren eigenen AJAX-Javascript-Code rollen. Hier ist ein Skript, das ich benutze:
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);
quelle
Vielleicht versuchen
return View("Index");
anstatt
return Index();
quelle