如何处理返回PartialView的ajax调用的控制器操作中的模型状态错误

时间:2010-02-14 15:44:53

标签: jquery asp.net-mvc ajax partial-views

我有一个POST控制器动作,它返回一个局部视图。一切似乎都很容易。但。我使用$.ajax()加载它,将类型设置为html。但是当我的模型验证失败时,我认为我应该抛出模型状态错误的错误。但我的回复总是返回500服务器错误。

如何在不返回Json的情况下报告模型状态错误。我仍然希望返回部分视图,我可以直接附加到某个HTML元素。

修改

我还想避免返回错误局部视图。这看起来像是客户端的成功。让客户端解析结果以查看它是否真正成功容易出错。设计人员可能会更改局部视图输出,仅此一项就会破坏功能。所以我想抛出异常,但是将正确的错误消息返回给ajax客户端。

2 个答案:

答案 0 :(得分:17)

解决方案

我必须编写两个独立的部分,自动完全按预期工作

因此,当控制器操作进程成功时它应该返回一个部分视图,并且当事情不好时它应该抛出一些错误细节,因此客户端的事情将区分成功和失败而不是总是处理成功。

有两个主要部分用于实现此目的:

  • 在出现问题时抛出的自定义异常类,以便我们可以区分出于任何原因随时可能发生的一般异常和与我们的处理相关的错误(最明显的是无效的模型状态)< / LI>
  • 异常操作过滤器,用于捕获我们的自定义异常并根据该异常准备结果;正如您将从代码中看到的,我们的自定义异常将保存有关模型状态错误的信息,因此此过滤器将能够返回自定义HTTP状态代码以及一些文本信息

详细信息然后......

  

外部链接:我的博客上也提供了所有这些信息(详细说明以及所有代码)。最新的代码更新将始终为published there

自定义异常类

这个课提供两件事

  1. 可以轻松区分模型状态错误和常规异常
  2. 提供我稍后可以使用的一些基本功能
  3. 此类稍后在我的自定义错误过滤器中使用。

    public class ModelStateException : Exception
    {
        public Dictionary<string, string> Errors { get; private set; }
    
        public ModelStateDictionary ModelState { get; private set; }
    
        public override string Message
        {
            get
            {
                if (this.Errors.Count > 0)
                {
                    return this.Errors.First().Value;
                }
                return null;
            }
        }
    
        private ModelStateException()
        {
            this.Errors = new Dictionary<string, string>();
        }
    
        public ModelStateException(ModelStateDictionary modelState) : this()
        {
            this.ModelState = modelState;
            if (!modelState.IsValid)
            {
                foreach (KeyValuePair<string, ModelState> state in modelState)
                {
                    if (state.Value.Errors.Count > 0)
                    {
                        this.Errors.Add(state.Key, state.Value.Errors[0].ErrorMessage);
                    }
                }
            }
        }
    }
    

    错误过滤器属性

    当存在任何模型状态错误时,此属性有助于根据HTTP错误代码将错误返回给客户端。

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
    public class HandleModelStateExceptionAttribute : FilterAttribute, IExceptionFilter
    {
        public void OnException(ExceptionContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }
    
            if (filterContext.Exception != null && typeof(ModelStateException).IsInstanceOfType(filterContext.Exception) && !filterContext.ExceptionHandled)
            {
                filterContext.ExceptionHandled = true;
                filterContext.HttpContext.Response.Clear();
                filterContext.HttpContext.Response.ContentEncoding = Encoding.UTF8;
                filterContext.HttpContext.Response.HeaderEncoding = Encoding.UTF8;
                filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
                filterContext.HttpContext.Response.StatusCode = 400;
                filterContext.HttpContext.Response.StatusDescription = (filterContext.Exception as ModelStateException).Message;
            }
        }
    }
    

    之后,我只是用我的属性装饰我的控制器动作并瞧。我确实在客户端上遇到了代码400的错误以及我在过滤器中设置的正确信息。然后将此信息显示给用户(当它与模型状态错误相关时,它会显示用户应修改的表单字段,以使表单有效)。

    [HandleModelStateException]
    public ActionResult AddComment(MyModel data)
    {
        // check if state is valid
        if (!this.ModelState.IsValid)
        {
            throw new ModelStateException(this.ModelState);
        }
        // get data from store
        return PartialView("Comment", /* store data */ );
    }
    

    这使得我的代码可以重用任何模型状态错误,并且这些错误将被发送到客户端。

    一个问题(现已解决)

    但是仍有一个与此代码相关的问题。当我的错误操作过滤器设置StatusDescription并且该字符串包含一些特殊字符(如Č)时,我会在客户端上出现垃圾。除非我使用IE(我使用的是版本8)。 FF和CH显示垃圾。这就是为什么我设置编码但它不起作用。如果有人为这种特殊性做了解决方法,我会非常乐意收听。
    如果我在内容本身返回错误消息,一切都很好。编码是正确的,我可以显示我想要的任何内容。

答案 1 :(得分:3)

试试这个。

public ActionResult DoAjaxAction(Entity entity)
{
   if(ModelState.IsValid)
   {
     return PartialView("Valid_View", entity);
   }
   else
   {
     return PartialView("Invalid_View", entity);
   } 

}