Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC - How to Preserve ModelState Errors Across RedirectToAction?

I have the following two action methods (simplified for question):

[HttpGet] public ActionResult Create(string uniqueUri) {    // get some stuff based on uniqueuri, set in ViewData.      return View(); }  [HttpPost] public ActionResult Create(Review review) {    // validate review    if (validatedOk)    {       return RedirectToAction("Details", new { postId = review.PostId});    }      else    {       ModelState.AddModelError("ReviewErrors", "some error occured");       return RedirectToAction("Create", new { uniqueUri = Request.RequestContext.RouteData.Values["uniqueUri"]});    }    } 

So, if the validation passes, i redirect to another page (confirmation).

If an error occurs, i need to display the same page with the error.

If i do return View(), the error is displayed, but if i do return RedirectToAction (as above), it loses the Model errors.

I'm not surprised by the issue, just wondering how you guys handle this?

I could of course just return the same View instead of the redirect, but i have logic in the "Create" method which populates the view data, which i'd have to duplicate.

Any suggestions?

like image 260
RPM1984 Avatar asked Jan 10 '11 00:01

RPM1984


People also ask

How do you preserve ModelState errors across RedirectToAction?

Basically, use TempData to save and restore the ModelState object. However, it's a lot cleaner if you abstract this away into attributes. E.g. If you also want to pass the model along in TempData (as bigb suggested) then you can still do that too.

Why is ModelState not valid MVC?

The error typically means that your Model doesn't meet the requirements to be validated. In your example, your FirstName , LastName , Email , Subject and Message properties are decorated with the [Required] attribute. This means that those values shouldn't be null and empty otherwise your condition if(ModelState.

Why is my ModelState IsValid false?

IsValid is false now. That's because an error exists; ModelState. IsValid is false if any of the properties submitted have any error messages attached to them. What all of this means is that by setting up the validation in this manner, we allow MVC to just work the way it was designed.

What is ModelState Clear () in MVC?

Clear() is required to display back your model object. If you are getting your Model from a form and you want to manipulate the data that came from the client form and write it back to a view, you need to call ModelState. Clear() to clean the ModelState values.


2 Answers

I had to solve this problem today myself, and came across this question.

Some of the answers are useful (using TempData), but don't really answer the question at hand.

The best advice I found was on this blog post:

http://www.jefclaes.be/2012/06/persisting-model-state-when-using-prg.html

Basically, use TempData to save and restore the ModelState object. However, it's a lot cleaner if you abstract this away into attributes.

E.g.

public class SetTempDataModelStateAttribute : ActionFilterAttribute {     public override void OnActionExecuted(ActionExecutedContext filterContext)     {         base.OnActionExecuted(filterContext);                  filterContext.Controller.TempData["ModelState"] =             filterContext.Controller.ViewData.ModelState;     } }  public class RestoreModelStateFromTempDataAttribute : ActionFilterAttribute {     public override void OnActionExecuting(ActionExecutingContext filterContext)     {         base.OnActionExecuting(filterContext);         if (filterContext.Controller.TempData.ContainsKey("ModelState"))         {             filterContext.Controller.ViewData.ModelState.Merge(                 (ModelStateDictionary)filterContext.Controller.TempData["ModelState"]);         }     } } 

Then as per your example, you could save / restore the ModelState like so:

[HttpGet] [RestoreModelStateFromTempData] public ActionResult Create(string uniqueUri) {     // get some stuff based on uniqueuri, set in ViewData.       return View(); }  [HttpPost] [SetTempDataModelState] public ActionResult Create(Review review) {     // validate review     if (validatedOk)     {         return RedirectToAction("Details", new { postId = review.PostId});     }       else     {         ModelState.AddModelError("ReviewErrors", "some error occured");         return RedirectToAction("Create", new { uniqueUri = Request.RequestContext.RouteData.Values["uniqueUri"]});     }    } 

If you also want to pass the model along in TempData (as bigb suggested) then you can still do that too.

like image 199
asgeo1 Avatar answered Sep 22 '22 12:09

asgeo1


You need to have the same instance of Review on your HttpGet action. To do that you should save an object Review review in temp variable on your HttpPost action and then restore it on HttpGet action.

[HttpGet] public ActionResult Create(string uniqueUri) {    //Restore    Review review = TempData["Review"] as Review;                 // get some stuff based on uniqueuri, set in ViewData.      return View(review); } [HttpPost] public ActionResult Create(Review review) {    //Save your object    TempData["Review"] = review;     // validate review    if (validatedOk)    {       return RedirectToAction("Details", new { postId = review.PostId});    }      else    {       ModelState.AddModelError("ReviewErrors", "some error occured");       return RedirectToAction("Create", new { uniqueUri = Request.RequestContext.RouteData.Values["uniqueUri"]});    }    } 

If you want this to work even if the browser is refreshed after the first execution of the HttpGet action, you could do this:

  Review review = TempData["Review"] as Review;     TempData["Review"] = review; 

Otherwise on refresh button object review will be empty because there wouldn't be any data in TempData["Review"].

like image 44
angularrocks.com Avatar answered Sep 19 '22 12:09

angularrocks.com