5

I wrote an application using ASP.NET MVC 5 framework. I am using a two way binding between the views and the ViewModels.

Since I am using two way binding, I get the benefit of client and server side validation which is cool. However, when I send a POST request to the server, and the request handler throws an exception, I want to redirect the user to the GET method.

When the redirect happens, I want to save the model state so that the page looks the same when I display the errors. I am able to save the state model and the errors using ActionFilters and TempData via this approach. However, when the request is redirected, from POST to GET the model state is saved as System.Web.Mvc.ModelStateDictionary object which is a key/value pair with all the user input that came from the POST request.

In order to present the page correctly to the end user, I need to bind the data in System.Web.Mvc.ModelStateDictionary to my own presentation model.

How can I bind the System.Web.Mvc.ModelStateDictionary object to my presentation object?

Here is how my code looks like

[ImportModelStateFromTempData]
public ActionResult show(int id)
{

    var prsenter = new UserProfileDetailsPresenter(id);

    ModelStateDictionary tmp = TempData["Support.ModelStateTempDataTransfer"];

    if(tmp != null)
    {
        // Some how map tmp to prsenter
    }

    return View(prsenter);

}

[HttpPost]
[ValidateAntiForgeryToken]
[ExportModelStateToTempData]
public ActionResult Update(int id, DetailsPresenter model)
{
    try
    {
        if (ModelState.IsValid)
        {
            var updater = new UpdateAddressServiceProvider(CurrentUser);

            updater.Handle(model.General);
        }

    }
    catch (Exception exception)
    {
        ModelState.AddModelError("error", exception.Message);
    } finally
    {
        return new RedirectResult(Url.Action("Show", new { Id = id }) + "#General");
    }
}
Jaylen
  • 39,043
  • 40
  • 128
  • 221

1 Answers1

4

If there's an error, don't redirect, just return the View.

[HttpPost]
[ValidateAntiForgeryToken]
[ExportModelStateToTempData]
public ActionResult Update(int id, DetailsPresenter model)
{
    try
    {
        if (ModelState.IsValid)
        {
            var updater = new UpdateAddressServiceProvider(CurrentUser);

            updater.Handle(model.General);
        }

        return new RedirectResult(Url.Action("Show", new { Id = id }) + "#General");
    }
    catch (Exception exception)
    {
        ModelState.AddModelError("error", exception.Message);

        // Return the named view directly, and pass in the model as it stands.
        return View("Show", model);
    }
}
krillgar
  • 12,596
  • 6
  • 50
  • 86
  • The "Show" view is expecting `UserProfileDetailsPresenter` object not `DetailsPresenter`. Instead of having to create `UserProfileDetailsPresenter` in every request, I rather redirect to the `Index` action and have it create the correct object and just bind the data. – Jaylen Dec 08 '16 at 19:16
  • You best bet is you make that call in both places (kudos for already having it in a method). You're going to be creating that `UserProfileDetailsPresenter` every time regardless. This is the correct way to do it in MVC. You don't want to put incorrect, malformed, or even potentially dangerous data into your database. – krillgar Dec 08 '16 at 19:24
  • So in that case there is no need to ExportModelStateToTempData, right? what would be the benefit of having it? – Jaylen Dec 08 '16 at 19:26
  • Correct. You already have your model, so that just adds extra processing. Are you asking if there's a benefit to keeping that around? – krillgar Dec 08 '16 at 19:27
  • yes. Is there any benifit of keeping `ExportModelStateToTempData` and `ImportModelStateToTempData`? – Jaylen Dec 08 '16 at 19:29
  • No that would be unnecessary if you aren't doing the Redirect. – krillgar Dec 08 '16 at 19:42
  • Although that did not answer my question but I accepted the answer because it gave me a workaround for my problem. Thank you – Jaylen Dec 08 '16 at 22:07
  • This is not a good approach because if the user try reloading the page after he/she encounter and error. He/She will get an error since there is no `GET` route to match the path that they are on i.e `Profile/Update` – Jaylen Dec 09 '16 at 01:58