小编典典

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

ajax

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

如何报告回模型状态错误而又不返回Json并返回任何结果。我仍然想返回可以直接附加到某些HTML元素的局部视图。

编辑

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


阅读 165

收藏
2020-07-26

共1个答案

小编典典

我必须编写两个独立的部分,它们 可以完全按预期 自动运行。

因此,当控制器动作过程成功时,它应该返回部分视图;当事情不正常时,它应该引发带有一些失败细节的错误,以便客户端的事情可以区分成功和失败,而不是总是处理成功。

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

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

然后到细节…

外部链接
:所有这些信息(详细说明以及所有代码)也都可以在我的博客上找到。最新的代码更新将始终在那里发布

自定义异常类

本课提供两件事

  1. 轻松区分模型状态错误和常规异常
  2. 提供一些我以后可以使用的基本功能

此类稍后在我的自定义错误过滤器中使用。

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;
        }
    }
}

之后,我只需用我的属性和voila装饰我的控制器动作。我确实在客户端上得到了错误,代码为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显示为垃圾。这就是为什么我设置编码但是不起作用。如果有人对此有特殊的解决方法,我将非常乐意听。

如果我在内容本身中返回错误消息,一切都很好。编码正确,我可以显示任何内容。

2020-07-26