Question

ASP.NET MVC 2.0

I'm doing Post-Redirect-Get, if I get errors on post, I need to include ModelErrors along for the ride to along -Redirect-Get route. I send it through 'TempData':

TempData["modelErors"] = 
    ModelState.
        Where(item => item.Value.Errors.Count > 0).
        ToDictionary(
            item => item.Key, 
            item => item.Value.Errors.Select(error=>error.ErrorMessage).ToList()
        );

And then reinsert it into a ModelState:

if (TempData.ContainsKey("modelErors")) {
    foreach (var errors in (IDictionary<string,IList<string>>) TempData["modelErors"]) {
        foreach (var error in errors.Value) {
            ModelState.AddModelError(errors.Key, error);
        }
    }
}

Is there a better way?

Was it helpful?

Solution

You should really only PRG after a successful post. Otherwise it's fine to return from the post if there's an error.

Otherwise you need to use cookies, session or request variables to store that information for the next request.

In ASP.NET MVC2 by default I think TempData uses Session state to store the information for the next request.

OTHER TIPS

I think that the most cleaner solution was to use ActionFilterAttribute like this :

public class RedirectErrorAttribute : ActionFilterAttribute
{
    #region Methods & Function
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext.Controller.TempData.ContainsKey("modelErrors"))
        {
            foreach (var errors in (Dictionary<string, List<string>>)filterContext.Controller.TempData["modelErrors"])
                foreach (var error in errors.Value)
                    filterContext.Controller.ViewData.ModelState.AddModelError(errors.Key, error);
        }

        base.OnActionExecuting(filterContext);
    }
    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        if (filterContext.Controller.ViewData.ModelState.Values.Any(x => x.Errors.Count > 0))
        {
            if (filterContext.Controller.TempData.ContainsKey("modelErrors"))
                filterContext.Controller.TempData.Remove("modelErrors");
            else
            {
                filterContext.Controller.TempData["modelErrors"] =
                    filterContext.Controller.ViewData.ModelState.
                    Where(item => item.Value.Errors.Count > 0).
                        ToDictionary(
                            item => item.Key,
                            item => item.Value.Errors.Select(error => error.ErrorMessage).ToList()
                        );

                filterContext.Controller.TempData.Keep("modelErrors");
            }
        }

        base.OnResultExecuted(filterContext);
    }

    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        base.OnResultExecuting(filterContext);
    }

    #endregion
}

After you just have to put your attribute on the top of the action that throw the error and the action that received the error like this :

[RedirectError]
public ActionResult Delete(Guid id)
{

[RedirectError]
public ActionResult Get(Guid id)
{

And that works like a charm with clean manageable code.

Hope this Help!

Julien

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top