ASP.NET MVC应用程序自定义错误页面未显示在共享主机环境中

Bry*_*yan 14 error-handling asp.net-mvc shared-hosting global-asax

我在我的共享主机上部署的ASP.NET MVC应用程序上遇到自定义错误问题.我创建了一个ErrorController并将以下代码添加到Global.asax以捕获未处理的异常,记录它们,然后将控制转移到ErrorController以显示自定义错误.此代码取自此处:

protected void Application_Error(object sender, EventArgs e)
{
    Exception ex = Server.GetLastError();
    Response.Clear();

    HttpException httpEx = ex as HttpException;
    RouteData routeData = new RouteData();
    routeData.Values.Add("controller", "Error");

    if (httpEx == null)
    {
        routeData.Values.Add("action", "Index");
    }
    else
    {
        switch (httpEx.GetHttpCode())
        {
            case 404:
                routeData.Values.Add("action", "HttpError404");
                break;
            case 500:
                routeData.Values.Add("action", "HttpError500");
                break;
            case 503:
                routeData.Values.Add("action", "HttpError503");
                break;
            default:
                routeData.Values.Add("action", "Index");
                break;
        }
    }

    ExceptionLogger.LogException(ex); // <- This is working. Errors get logged

    routeData.Values.Add("error", ex);
    Server.ClearError();
    IController controller = new ErrorController();
    // The next line doesn't seem to be working
    controller.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}
Run Code Online (Sandbox Code Playgroud)

Application_Error肯定会触发,因为日志记录工作正常,但我没有显示自定义错误页面,而是获得Go Daddy泛型.从博客文章的标题中取出上面的代码,我注意到它使用了MVC框架的Release Candidate 2.在1.0中有什么变化使最后一行代码不起作用?像往常一样,它在我的机器上运行良好.

任何建议将不胜感激.

编辑:忘了提到我已经尝试了Web.config(Off,On和RemoteOnly)中的customErrors模式的所有3种可能性.无论此设置如何,结果都相同.

编辑2:我也尝试过在Controller类上有[HandleError]装饰的情况.

更新:我已经找到并修复了404.Go Daddy的托管控制中心的"设置"面板中有一部分可以控制404行为,默认情况下是显示其通用页面,显然这会覆盖任何Web.config设置.所以我的自定义404页面现在按预期显示.但是,500和503仍然无法正常工作.如果Sql Server抛出异常,我在HomeController中有代码来获取内容的静态文本版本,如下所示:

public ActionResult Index()
{
    CcmDataClassesDataContext dc = new CcmDataClassesDataContext();

    // This might generate an exception which will be handled in the OnException override
    HomeContent hc = dc.HomeContents.GetCurrentContent();

    ViewData["bodyId"] = "home";
    return View(hc);
}

protected override void OnException(ExceptionContext filterContext)
{
    // Only concerned here with SqlExceptions so an HTTP 503 message can
    // be displayed in the Home View. All others will bubble up to the
    // Global.asax.cs and be handled/logged there.
    System.Data.SqlClient.SqlException sqlEx =
        filterContext.Exception as System.Data.SqlClient.SqlException;
    if (sqlEx != null)
    {
        try
        {
            ExceptionLogger.LogException(sqlEx);
        }
        catch
        {
            // couldn't log exception, continue without crashing
        }

        ViewData["bodyId"] = "home";
        filterContext.ExceptionHandled = true;
        HomeContent hc = ContentHelper.GetStaticContent();
        if (hc == null)
        {
            // Couldn't get static content. Display friendly message on Home View.
            Response.StatusCode = 503;
            this.View("ContentError").ExecuteResult(this.ControllerContext);
        }
        else
        {
            // Pass the static content to the regular Home View
            this.View("Index", hc).ExecuteResult(this.ControllerContext);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是尝试获取静态内容的代码:

public static HomeContent GetStaticContent()
{
    HomeContent hc;

    try
    {
        string path = Configuration.CcmConfigSection.Config.Content.PathToStaticContent;
        string fileText = File.ReadAllText(path);
        string regex = @"^[^#]([^\r\n]*)";
        MatchCollection matches = Regex.Matches(fileText, regex, RegexOptions.Multiline);
        hc = new HomeContent
            {
                ID = Convert.ToInt32(matches[0].Value),
                Title = matches[1].Value,
                DateAdded = DateTime.Parse(matches[2].Value),
                Body = matches[3].Value,
                IsCurrent = true
            };
    }
    catch (Exception ex)
    {
        try
        {
            ExceptionLogger.LogException(ex);
        }
        catch
        {
            // couldn't log exception, continue without crashing
        }
        hc = null;
    }

    return hc;
}
Run Code Online (Sandbox Code Playgroud)

我已经验证,如果我更改连接字符串以生成SqlException,则代码会正确记录错误,然后抓取并显示静态内容.但是,如果我还在Web.config中更改静态文本文件的路径来测试主视图的503版本,那么我得到的是一个除"服务不可用"之外的页面.而已.没有自定义503消息与网站的外观和感觉.

有没有人对代码的改进有任何建议可能会有所帮助?将不同的标题添加到HttpResponse会有帮助吗?还是Go Daddy狠狠地劫持了503s?

Bry*_*yan 29

我找到了解决方案,而且非常简单.事实证明问题实际上是在IIS7中.在Visual Studio中调试此问题时,我看到了之前没有注意到的HttpResponse对象的属性:

public bool TrySkipIisCustomErrors { get; set; }
Run Code Online (Sandbox Code Playgroud)

这引导我到我最近的搜索引擎,由Rick Strahl发布了一篇很棒的博客文章,另一篇关于angrypets.com的博客文章以及这里的问题.这些链接比我更好地解释了血腥细节,但Rick的帖子引用了它很好的说法:

这里真正的混乱是因为错误被ASP.NET捕获,但最终仍由IIS处理,它查看500状态代码并返回库存IIS错误页面.

在集成模式下,此行为似乎也特定于IIS7.来自msdn:

在IIS 7.0中以经典模式运行时,TrySkipIisCustomErrors属性的默认值为true.在集成模式下运行时,TrySkipIisCustomErrors属性的默认值为false.

所以我最终要做的就是Response.TrySkipIisCustomErrors = true;在任何设置Response.StatusCode为500或503的代码之后添加,现在一切都按设计运行.

  • 这让我感到疯狂.非常感谢.放置"Response.TrySkipIisCustomErrors = true;" 在我的错误控制器中设置响应代码之后立即对我有用.这是误导性的,因为我的本地IIS 7实例正在呈现我的自定义错误视图,但远程登台服务器却没有.= P (2认同)