首页 > 解决方案 > Asp.Net 显示与 $ajax 一起使用的默认错误页面

问题描述

我有一个混合的 asp.net 网络表单/mvc 应用程序。当发生 500 错误时,我想执行以下操作:

  1. 如果错误发生在 Web 表单上,则显示自定义 *.aspx 页面。
  2. 如果错误发生在返回视图的控制器操作中,则显示自定义 mvc 视图。
  3. 如果错误发生在响应 $ajax 请求的控制器操作中,则显示用 javascript 构建的自定义消息。 这第三项是我遇到问题的地方。

在所有情况下,我都想只记录一次错误。我在 global.asax Application_OnError 事件过程中记录 Web 表单错误。对于我的 mvc 控制器,我已将此调用添加到 Application_Start 事件过程:

GlobalFilters.Filters.Add(new HandleErrorAttribute());

然后创建一个基类,应用程序中的所有控制器都从该基类派生。在这个基类中,我在 OnException 事件过程中记录错误。错误记录工作正常。

对于第 1 项,自定义 *.aspx 页面,我将其添加到我的 web.config 中,它成功地将所有 webforms 错误重定向到我的自定义错误页面:

<customErrors mode="On" defaultRedirect="MyFolder/MyDefaultErrorPage.aspx" 

对于第 2 项,自定义 mvc 视图,我发现我需要在 web.config 的 httpErrors 元素中捕获 IIS 错误。这成功地将所有控制器错误重定向到我的自定义错误视图:

    <httpErrors errorMode="Custom" existingResponse="Replace">
  <clear />
  <error statusCode="500" path="/MyFolder/MyErrorView" responseMode="Redirect"/>
</httpErrors>

以上所有工作都很好,但不幸的是,拦截 IIS 500 错误的第 2 项导致所有 $ajax 调用都返回成功:事件处理程序而不是错误:事件处理程序,即使控制器操作抛出异常也是如此。例如,我从 javascript 调用控制器操作:

$.ajax({
            type: "GET",
            url: '/MyFolder/MyControllerAction',
            success: function (data) {
                do_something(data);
            },
            error: function (XMLHttpRequest, ajaxOptions, ex) {
                show_error_message(XMLHttpRequest, ex); 
            }

问题是当 MyControllerAction 抛出异常时,调用的是“success:”事件处理程序,而不是“error:”事件处理程序。如果我从 web.config 中删除拦截 IIS 500 错误的节点,我会在 $ajax 调用中点击所需的“错误:”事件处理程序,但它显示默认的 IIS 500 错误页面,而不是我的自定义错误页面。

有什么方法可以为我的所有控制器错误显示自定义错误页面并且仍然有错误:当我从 javascript 执行 $ajax 调用时,事件处理程序会触发?

更新:如果我创建了一个从 HandleErrorAttribute 派生的自定义类并在 GlobalFilters.Filters.Add() 中使用它,我就到了那里。这会导致“错误:”事件在我的 javascript $ajax 调用中触发。问题是 XMLHttpRequest.responseText 不包含我的自定义消息(它只是说“页面无法显示,因为发生了内部服务器错误。”)。如果有任何方法可以将自定义消息传递给返回给 $.ajax 调用的 XMLHttpRequest.responseText?

public class MyCustomHandleErrorAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        if(filterContext.HttpContext.Request.IsAjaxRequest()
            && filterContext.Exception != null)
        {
            //500 is needed so that $ajax "error:" event handler is hit, instead of "success:"
            filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

            filterContext.Result = new JsonResult
            {
                JsonRequestBehavior = JsonRequestBehavior.AllowGet,
                Data = new
                {
                    Message = "Custom message will go here"
                }
            };

            //this stops Application_OnError from firing
            filterContext.ExceptionHandled = true;
        }
        else
        {
            filterContext.Result = new ContentResult()
            {
                Content = "<html><body>Custom message will go here</body></html>",
                ContentType = "text/html"
            };
            filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.OK;
            filterContext.ExceptionHandled = true;
        }
    }

标签: javascriptasp.netasp.net-mvcerror-handling

解决方案


总结一下:当服务器发生运行时错误时,我希望能够将自定义错误消息传递给 $ajax 事件处理程序(我不想要默认的 IIS 错误消息),并且我想要 $ajax “错误:”函数触发(不是“成功:”函数)。

上述问题中概述的自定义 HandleErrorAttribute 有效,但还需要进行两项更改才能覆盖默认的 IIS 错误消息:

  1. 将此行添加到自定义属性的 OnException 过程中的第一个“if”块(处理 ajax 错误的“if”块):

    filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;

  2. 将此添加到 web.config 中的 httpErrors 标记:

    errorMode="自定义" existingResponse="PassThrough"

existingResponse 的值也可以是“Auto”

我的最终自定义属性如下所示:

public class MyCustomHandleErrorAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        //log the error
        if (filterContext.Exception != null)
        {
            //note that Application_Error makes the same call for errors it handles.
            //this method and Application_Error are the only two places in the application where errors are handled.
            MyErrorLogger(filterContext.Exception.GetBaseException());
        }

        if (filterContext.HttpContext.Request.IsAjaxRequest()
            && filterContext.Exception != null)
        {
            //this is an error from a $ajax call.
            //500 is needed so that $ajax "error:" function is hit, instead of "success:"
            filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

            filterContext.Result = new JsonResult()
            {
                Data = new 
                {
                    MyCustomMessage="A custom error message",
                    IsException = true
                }
            };

            //this stops Application_OnError from firing
            filterContext.ExceptionHandled = true;

            //this stops the web site from using the default IIS 500 error. 
            //in addition must set existingResponse="PassThrough" or "Auto" in web.config httpErrors element.
            filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
        }
        else
        {
            //this is an error in a controller method that returns a view
            filterContext.Result = new RedirectToRouteResult(
                new System.Web.Routing.RouteValueDictionary
                {
                    {"action", "Error500" },
                    {"controller", "Error" },
                    { "Message", "My custom message here."}
                });
            filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.OK;
            filterContext.ExceptionHandled = true;
        }
    }
}

上面创建的自定义错误消息在客户端解析为“error:”函数的“XMLHttpRequest.responseText”参数中的 JSON 对象:

error: function (XMLHttpRequest, ajaxOptions, ex) {
    alert(jQuery.parseJSON(XMLHttpRequest.responseText).MyCustomMessage);
        }

推荐阅读