簡體   English   中英

如何使用 RedirectToAction 維護 ModelState?

[英]How can I maintain ModelState with RedirectToAction?

如果我的ModelState存在錯誤,如何在不丟失我的ModelState信息的情況下返回不同操作的結果或將用戶移動到不同的操作?

場景是; Delete操作接受來自我的Index操作/視圖呈現的 DELETE 表單的 POST。 如果Delete有錯誤,我想將用戶移回Index Action/View 並顯示由Delete操作存儲在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");
}

將您的視圖數據存儲在TempData並在您的Index操作中從那里檢索它(如果存在)。

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

   RedirectToAction( "Index" );
}

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

     ...
 }

[編輯] 我檢查了 MVC 的在線源,似乎控制器中的ViewData是可設置的,因此將所有ViewData ,包括ModelState傳輸到 Index 操作可能是最簡單的。

使用動作過濾器(PRG 模式)(就像使用屬性一樣簡單)

這里這里提到。

請注意,tvanfosson 的解決方案並不總是有效,但在大多數情況下應該沒問題。

該特定解決方案的問題在於,如果您已經擁有任何 ViewData 或 ModelState,您最終會用先前請求的狀態覆蓋它們。 例如,新請求可能有一些與傳遞給操作的無效參數相關的模型狀態錯誤,但這些錯誤最終會被隱藏,因為它們被覆蓋了。

它可能無法按預期工作的另一種情況是,如果您有一個 Action Filter 初始化了一些 ViewData 或 ModelState 錯誤。 同樣,它們將被該代碼覆蓋。

我們正在研究 ASP.NET MVC 的一些解決方案,它們可以讓您更輕松地合並來自兩個請求的狀態,因此請繼續關注。

謝謝,伊隆

如果這對我使用 PRG 使用 @bob 推薦的解決方案的任何人有用:

參見第 13 項 -> 鏈接

在執行RedirectToAction("Action")時,我在控制器操作中從 TempData 手動寫入和檢查/加載了消息在 VeiwBag 中傳遞到視圖的附加問題。 為了簡化(並使其可維護),我稍微擴展了這種方法來檢查和存儲/加載其他數據。 我的操作方法看起來像:

 [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 提供的鏈接所做的事情的一個非常明顯的擴展 - 但在我什至想到以這種方式處理它之前,我不得不偶然發現這個線程。

請不要因為這個答案而質疑我。 這是一個合理的建議。

使用 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);

也許試試

return View("Index");

代替

return Index();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM