How can I maintain ModelState with RedirectToAction?

前端 未结 6 936
日久生厌
日久生厌 2020-12-02 05:57

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?

6条回答
  •  有刺的猬
    2020-12-02 06:36

    In case this is useful to anyone I used @bob 's recommended solution using PRG:

    see item 13 -> link.

    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"). 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.

提交回复
热议问题