Скопируйте ошибки ModelState в TempData и отобразите их в представлении

Большинство моих методов действий возвращают PartialViews в случае успеха и RedirectToAction в случае неудачи. Для этого я хотел бы скопировать ошибки состояния модели в TempData, чтобы я мог отображать их пользователю. Я прочитал здесь несколько вопросов по SO и некоторым внешним ссылкам, но ни один из них не помог мне ... Я украшаю ActionMethod атрибутом ModelStateToTempData из MvcContrib, а затем отображаю его в представлении следующим образом: (это всего лишь прототип )

        @if (TempData.Count > 0)
        {
            foreach (var obj in TempData)
            {
                var errors = ((ModelStateDictionary)obj.Value).Values;
                foreach (var error in errors)
                {
                <div style="position:absolute; background:Black; color:White; top:250px; left:550px;">
                    <span style="margin-bottom:5px; display:block; height:25px;">@error.Value</span>
                </div>
                }
            }
        }

Вместо того, чтобы отображать саму ошибку, я продолжаю получать System.Web.Mvc.ValueProviderResult. Я знаю, что это неправильно, и в конечном итоге я хотел бы отфильтровать ошибки состояния модели в словарь внутри TempData, но пока я просто хочу, чтобы строка ошибки отображалась в представлении.

P.S: Я пробовал делать это вручную без атрибута MvcContrib, и получил тот же результат. Но я предпочитаю использовать свой собственный код, чтобы иметь больший контроль над всей проблемой.

Какие-либо предложения?


person Kassem    schedule 07.05.2011    source источник


Ответы (3)


Хорошо Попробовав миллион вещей, я сам нашел ответ ... :)

if (TempData["ModelErrors"] == null)
    TempData.Add("ModelErrors", new List<string>());
foreach (var obj in ModelState.Values)
{
    foreach (var error in obj.Errors)
    {
        if(!string.IsNullOrEmpty(error.ErrorMessage))
            ((List<string>)TempData["ModelErrors"]).Add(error.ErrorMessage);
    }
}
return RedirectToAction("Index", "Home");

И в представлении:

    <div id="validationMessages">
        @{
            var errors = (List<string>)TempData["ModelErrors"];
        }
        @if (errors != null && errors.Count() > 0)
        {
            <div style="position:absolute; background:Black; color:White; top:250px; left:550px;">
                @foreach (var error in errors)
                { 
                   <span style="margin-bottom:5px; display:block; height:25px;">@error</span> 
                }
            </div>
        }
    </div>

ОБНОВЛЕНИЕ:

Вот он внутри ActionFilter:

public class CopyModelStateErrorsToTempData : ActionFilterAttribute
{
    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))
            {
                if (filterContext.Controller.TempData["ModelErrors"] == null)
                    filterContext.Controller.TempData.Add("ModelErrors", new List<string>());
                foreach (var obj in filterContext.Controller.ViewData.ModelState.Values)
                {
                    foreach (var error in obj.Errors)
                    {
                        if (!string.IsNullOrEmpty(error.ErrorMessage))
                            ((List<string>)filterContext.Controller.TempData["ModelErrors"]).Add(error.ErrorMessage);
                    }
                }
            }
        }

        base.OnActionExecuted(filterContext);
    }
}
person Kassem    schedule 07.05.2011

Я начал идти по этой дороге, а потом прочитал ваш ответ. Я объединил их в следующие файлы:

TempDataDictionaryExtensions.cs

Я создал методы расширения, чтобы выполнять грязную работу с TempData, потому что я чувствовал, что они не принадлежат самому фильтру действий.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;

namespace Project.Web.UI.Domain
{
    public static class TempDataDictionaryExtensions
    {
        private const string _ModelStateErrorsKey = "ModelStateErrors";

        public static IEnumerable<string> GetModelErrors(this TempDataDictionary instance)
        {
            return TempDataDictionaryExtensions.GetErrorsFromTempData(instance);
        }

        public static void AddModelError(this TempDataDictionary instance, string error)
        {
            TempDataDictionaryExtensions.AddModelErrors(instance, new List<string>() { error });
        }

        public static void AddModelErrors(this TempDataDictionary instance, IEnumerable<string> errors)
        {
            TempDataDictionaryExtensions.AddErrorsToTempData(instance, errors);
        }

        private static List<string> GetErrorsFromTempData(TempDataDictionary instance)
        {
            object tempObject = instance.FirstOrDefault(x => x.Key == TempDataDictionaryExtensions._ModelStateErrorsKey);
            if (tempObject == null)
            {
                return new List<String>();
            }
            List<string> tempErrors = instance.FirstOrDefault(x => x.Key == TempDataDictionaryExtensions._ModelStateErrorsKey).Value as List<string>;
            if (tempErrors == null)
            {
                return new List<String>();
            }
            return tempErrors;
        }

        private static void AddErrorsToTempData(TempDataDictionary instance, IEnumerable<string> errors)
        {
            List<string> tempErrors;

            object tempObject = instance.FirstOrDefault(x => x.Key == TempDataDictionaryExtensions._ModelStateErrorsKey);
            if (tempObject == null)
            {
                tempErrors = new List<String>();
            }
            else
            {
                tempErrors = instance.FirstOrDefault(x => x.Key == TempDataDictionaryExtensions._ModelStateErrorsKey).Value as List<string>;
                if (tempErrors == null)
                {
                    tempErrors = new List<String>();
                }
            }

            tempErrors.AddRange(errors);

            instance[TempDataDictionaryExtensions._ModelStateErrorsKey] = tempErrors;
        }
    }
}

TempDataModelStateAttribute.cs

Мой оригинал скопировал ошибки из TempData обратно в ModelState до выполнения ActionResult через OnResultExecuting. Это комбинация их копирования в TempData и обратно.

using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;

namespace Project.Web.UI.Domain
{
    public class TempDataModelStateAttribute : ActionFilterAttribute
    {
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            IEnumerable<string> modelErrors = ((Controller)filterContext.Controller).TempData.GetModelErrors();
            if (modelErrors != null
                && modelErrors.Count() > 0)
            {
                modelErrors.ToList()
                           .ForEach(x => ((Controller)filterContext.Controller).ModelState.AddModelError("GenericError", x));
            }
            base.OnResultExecuting(filterContext);
        }

        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            if (!filterContext.Controller.ViewData.ModelState.IsValid)
            {
                if (filterContext.Result is RedirectResult
                    || filterContext.Result is RedirectToRouteResult)
                {
                    List<string> errors = new List<string>();
                    foreach (var obj in filterContext.Controller.ViewData.ModelState.Values)
                    {
                        foreach (var error in obj.Errors)
                        {
                            errors.Add(error.ErrorMessage);
                        }
                    }
                    ((Controller)filterContext.Controller).TempData.AddModelErrors(errors); 
                }
            }

            base.OnActionExecuted(filterContext);
        }
    }
}
person Erik Philips    schedule 29.04.2012

Вам следует серьезно рассмотреть эту концепцию: http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx#prg

person ITmeze    schedule 19.06.2012
comment
Какая концепция? Не могли бы вы уточнить, пожалуйста? - person Kassem; 19.06.2012
comment
Похоже, вы пытаетесь реализовать концепцию / шаблон под названием PRG (Post / Redirect / Get). Следующая ссылка, которую я предоставил, или проверка проекта MvcContrib на CodePlex, должна дать вам хорошее объяснение самого Pattern плюс примеры реализации - person ITmeze; 20.07.2012