关于jquery:如何在返回partialView的Ajax调用的控制器操作中处理模型状态错误

How to handle model state errors in ajax-invoked controller action that returns a PartialView

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

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

编辑

我也希望避免返回错误部分视图。这在客户机上看起来是成功的。让客户机分析结果以查看它是否真正成功,这很容易出错。设计人员可能会更改部分视图输出,而这将单独破坏功能。所以我想抛出一个异常,但返回到Ajax客户机的错误消息是正确的。


解决方案

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

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

有两个主要部分用于实现这一点:

  • 一个定制的异常类,在发生错误时抛出,这样我们就可以区分可能因任何原因随时发生的一般异常和与处理相关的错误(最明显的是模型状态无效)。
  • 异常操作筛选器,它捕获自定义异常并根据该异常准备结果;从代码中可以看到,自定义异常将包含有关模型状态错误的信息,因此该筛选器将能够返回自定义HTTP状态代码以及一些文本信息。

接下来是细节…

External link: All this information (detailed explanation as well as all the code) is also available on my blog. Latest code updates will always be published there.

自定义异常类

这个班提供了两件事

  • 使区分模型状态错误和常规异常变得简单
  • 提供一些我以后可以使用的基本功能
  • 这个类稍后在我的自定义错误过滤器中使用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    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错误代码将错误返回到客户端。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    [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;
            }
        }
    }

    之后,我简单地用我的属性和voila修饰我的控制器操作。我确实在客户端上得到了代码400的错误,并更正了我在过滤器中设置的信息。然后将这些信息显示给用户(当与模型状态错误相关时,将显示用户应修改哪些表单字段以使表单有效的信息)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    [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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public ActionResult DoAjaxAction(Entity entity)
    {
       if(ModelState.IsValid)
       {
         return PartialView("Valid_View", entity);
       }
       else
       {
         return PartialView("Invalid_View", entity);
       }

    }