Search code examples
c#asp.net-mvcmodelstatepost-redirect-get

How should a refresh be handled after a redirect on ModelState failure using the strict PRG pattern?


So I've always used the loose PRG pattern where you return a view on ModelState validation failure from a POST action. However, it's always bothered me that I have to build the model not only in the GET action, but also rebuild it again in the POST action on failure. I've used different methods of doing the rebuilding using "view model builders" or just a private function within the controller that builds the view model for both actions, but these still bother me as well.

After reading this article by Ben Foster (http://benfoster.io/blog/automatic-modelstate-validation-in-aspnet-mvc), it makes a lot more sense to just rely on the GET action to build your view model - keeping it one area of the code - and then use the necessary action filters to save the ModelState for rendering when you are redirected back to GET on a failed POST.

So I've implemented that using the filters Ben mentions in his article like below. However, I am curious what happens if the user refreshes after they have been redirected back to the GET on a ModelState failure? How would I differentiate between someone accessing the GET directly versus a ModelState failure? Currently the ModelState would be gone if a user refreshes at that point. Is that the correct action, or should the user continue to see the errors until they POST with valid data? Essentially, should they be seeing the data that is in the database, or should they continue to see the changes they made when POSTing?

[ImportModelStateFromTempData]
public ActionResult Edit(int id)
{
    // in a real application this would be retrieved from the db
    var editView = new EditView()
    {
        UserId = id,
        Name = "John Doe",
        Age = 20,
        Message = "Hello world"
    };

    return View(editView);
}

[HttpPost]
[ValidateModelState]
public ActionResult Edit(EditCommand editCommand)
{
    // save to db here in real application

    return RedirectToAction("Success");
}

Solution

  • I use the same [ImportModelStateFromTempData] filter in a couple projects, and it works great.

    In my opinion, if the user refreshes, you shouldn't preserve any model state errors. The user is asking for a fresh view of that page, and it'd be frustrating to never be able to get a clean view. In same vain that refreshing after a POST shouldn't resubmit a form, refreshing after a GET shouldn't preserve a POST.