ASP.NET MVC全局错误处理

bev*_*qua 10 c# error-handling asp.net-mvc-3

我有一个自定义HandleError属性来处理MVC管道上的错误; 我有一个protected void Application_Error(object sender, EventArgs e)方法Global.asax来处理来自管道外部的错误.

我遇到过一个我不知道可能的情景; 在实现DI时,存在对a的依赖性connectionString,该依赖性取自应用程序配置文件.

由于连接字符串尚不存在,因此在创建控制器时会出错,这通常会使Application_Error处理程序触发,并呈现正确的错误页面(通过将部分视图呈现为字符串并将其作为响应发送,以防万一这失败了它只是写了"致命异常."的回应.

除了在这种情况下,我得到了虚假的默认ASP.NET"运行时错误"黄色死亡屏幕.告诉我:

运行时错误

说明:服务器上发生应用程序错误.此应用程序的当前自定义错误设置可防止查看应用程序错误的详细信息.

详细信息:要在本地服务器计算机上查看此特定错误消息的详细信息,请在位于当前Web应用程序根目录中的"web.config"配置文件中创建标记.然后,此标记应将其"mode"属性设置为"RemoteOnly".要使详细信息可在远程计算机上查看,请将"mode"设置为"Off".

我没有defaultRedirect设置customErrors,也没有设置Off,因为我不想重定向,而是在用户所在的同一页面上呈现错误,避免了不必要的重定向.

我该如何处理这样的场景?甚至是什么原因导致它以这种方式运行而不像控制器之外的任何其他错误?

我意识到它不太可能经常发生,但我希望能够阻止YSOD(部分原因是因为我想隐藏我正在使用的技术,但主要是因为它不是很漂亮,也不是用户友好的)

我甚至尝试为UnhandledExceptions注册一个处理程序,但它也没有触发.

AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
Run Code Online (Sandbox Code Playgroud)

最终产生这个的代码是:

return ConfigurationManager.ConnectionStrings[key].ConnectionString;,这里ConnectionStrings[key]null.

更新

这是应用程序错误的处理方式:

    protected void Application_Error(object sender, EventArgs e)
    {
        this.HandleApplicationError(new ResourceController());
    }

    public static void HandleApplicationError(this HttpApplication application, BaseController controller)
    {
        if (application == null)
        {
            throw new ArgumentNullException("application");
        }
        if (controller == null)
        {
            throw new ArgumentNullException("controller");
        }
        application.Response.Clear();
        Exception exception = application.Server.GetLastError();
        LogApplicationException(application.Response, exception);
        try
        {
            RenderExceptionViewResponse(application, exception, controller);
        }
        catch (Exception exceptionRenderingView) // now we're in trouble. let's be as graceful as possible.
        {
            RenderExceptionTextResponse(application, exceptionRenderingView);
        }
        finally
        {
            application.Server.ClearError();
        }
    }

    private static void LogApplicationException(HttpResponse response, Exception exception)
    {
        if (exception is HttpException)
        {
            HttpException httpException = (HttpException)exception;
            if (httpException.GetHttpCode() == (int)HttpStatusCode.NotFound)
            {
                _log.Debug(Resources.Error.WebResourceNotFound, httpException);
                response.Status = Resources.Constants.NotFound;
                return;
            }
        }
        _log.Error(Resources.Error.UnhandledException, exception);
    }

    private static void RenderExceptionViewResponse(HttpApplication application, Exception exception, BaseController controller)
    {
        if (!RenderAsJsonResponse(application, Resources.User.UnhandledExceptionJson))
        {
            ErrorViewModel model = WebUtility.GetErrorViewModel(exception);
            string result = controller.RenderViewToString(Resources.Constants.ErrorViewName, model);
            application.Response.Write(result);
        }
    }

    private static void RenderExceptionTextResponse(HttpApplication application, Exception exceptionRenderingView)
    {
        application.Response.Clear();

        if (!RenderAsJsonResponse(application, Resources.User.FatalExceptionJson))
        {
            application.Response.Write(Resources.User.FatalException);
        }
        _log.Fatal(Resources.Error.FatalException, exceptionRenderingView);
    }

    private static bool RenderAsJsonResponse(HttpApplication application, string message)
    {
        if (application.Request.IsAjaxRequest())
        {
            application.Response.Status = Resources.Constants.HttpSuccess;
            application.Response.ContentType = Resources.Constants.JsonContentType;
            application.Response.Write(message);
            return true;
        }
        return false;
    }
Run Code Online (Sandbox Code Playgroud)

这是我用来装饰我的基本控制器的属性:

public class ErrorHandlingAttribute : HandleErrorAttribute
{
    public Type LoggerType { get; set; }

    public ErrorHandlingAttribute()
        : this(typeof(ErrorHandlingAttribute))
    {
    }

    public ErrorHandlingAttribute(Type loggerType)
    {
        LoggerType = loggerType;
    }

    public override void OnException(ExceptionContext filterContext)
    {
        if (filterContext.ExceptionHandled)
        {
            return;
        }
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            OnAjaxException(filterContext);
        }
        else
        {
            OnRegularException(filterContext);
        }
    }

    internal protected void OnRegularException(ExceptionContext filterContext)
    {
        Exception exception = filterContext.Exception;

        ILog logger = LogManager.GetLogger(LoggerType);
        logger.Error(Resources.Error.UnhandledException, exception);

        filterContext.HttpContext.Response.Clear();

        ErrorViewModel model = WebUtility.GetErrorViewModel(exception);
        filterContext.Result = new ViewResult
        {
            ViewName = Resources.Constants.ErrorViewName,
            ViewData = new ViewDataDictionary(model)
        };
        filterContext.ExceptionHandled = true;
    }

    internal protected void OnAjaxException(ExceptionContext filterContext)
    {
        Exception exception = filterContext.Exception;

        ILog logger = LogManager.GetLogger(LoggerType);
        logger.Error(Resources.Error.UnhandledAjaxException, exception);

        filterContext.HttpContext.Response.Clear();
        filterContext.HttpContext.Response.Status = Resources.Constants.HttpSuccess;

        string errorMessage = WebUtility.GetUserExceptionMessage(exception, true);

        filterContext.Result = new ExceptionJsonResult(new[] { errorMessage });
        filterContext.ExceptionHandled = true;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的customErrors:

<customErrors mode="On" />
Run Code Online (Sandbox Code Playgroud)

正如你所看到的那些相当广泛,但是在访问不存在的ConnectionStrings地方时它们甚至都没有发射ConnectionString; 这有点令人费解.

触发任何控制器包含的异常或不在控制器内的异常,所以我不明白为什么这种情况有所不同.

jga*_*fin 7

Application_Error可能是在ASP.NET WebForms中处理错误的推荐方法.但不是在MVC中.

我们得到了错误过滤器,可以解决我们的错误.过滤器的问题是它只在调用控制器时才有效.404和401错误(未找到和授权)以及您的数据库连接问题是哪个问题.

customErrors是去这里的方式.我不明白为什么重定向应该是一个问题?

我正在博客文章中进行适当的错误处理:http://blog.gauffin.org/2011/11/how-to-handle-errors-in-asp-net-mvc/

  • 重定向自定义错误是一个问题,因为它基本上回到了浏览器(或机器人)的缺失页面"是的,这个页面没问题,但请暂时访问这个其他页面",然后下一页说"不,我是不行,我不存在".然而,您的浏览器和机器人(例如谷歌机器人)都会将缺失的页面存储为真实页面,并在谷歌的情况下继续尝试对其进行索引,因为临时重定向表示将来会继续检查原始页面.这就是为什么在请求的原始URL上返回状态404非常重要的原因. (9认同)

Dar*_*rov 0

您没有显示您如何准确处理 Application_Error 事件中的错误,也没有显示您的自定义 HandleAttribute 是如何实现的,因此很难猜测问题可能是什么。实现 Application_Error 时需要注意的一个常见问题是,您渲染了一些本身会引发错误的错误视图。例如,考虑一个依赖于布局的错误视图,在该布局内,您调用一个Html.Action助手来呈现另一个操作的内容,该操作本身进行数据库访问和其他操作。您的错误视图应该尽可能静态。理想情况下,您应该为它们设置不同的布局,以避免出现此类情况。

我还建议您查看以下处理错误的方法。