将ModelState错误复制到TempData&在视图中显示它们

时间:2011-05-07 08:27:47

标签: asp.net-mvc asp.net-mvc-3 modelstate tempdata

我的大多数操作方法都会在成功时返回PartialViews,而RedirectToAction会在失败时返回。为此,我想将模型状态错误复制到TempData中,以便我可以将它们显示给用户。我在这里看了几个关于SO和一些外部链接的问题,但没有一个对我有用......我正在用MvcContrib的ModelStateToTempData属性装饰ActionMethod,然后在视图中显示如下:(这个只是一个原型)

        @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属性的情况下手动完成,我得到了相同的结果。但我更喜欢使用我自己的代码,这样我就可以更好地控制整个问题。

有什么建议吗?

3 个答案:

答案 0 :(得分: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);
    }
}

答案 1 :(得分:1)

我开始走这条路,然后读你的答案。我将它们合并到以下文件中:

<强> TempDataDictionaryExtensions.cs

我创建了扩展方法来对TempData进行脏工作,因为我觉得它不属于Action Filter本身。

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执行ActionResult之前,将错误从ModelState复制回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);
        }
    }
}

答案 2 :(得分:0)