ASP.NET MVC 应用程序自定义错误页面在共享托管环境中不显示

发布于 2024-08-10 04:03:16 字数 4853 浏览 3 评论 0 原文

我在共享主机上部署的 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));
}

Application_Error 肯定会触发,因为日志记录工作正常,但我没有显示我的自定义错误页面,而是显示了 GoDaddy 通用错误页面。从博客文章的标题中摘取了上面的代码,我注意到它使用了 MVC 框架的 Release Candidate 2。 1.0 中是否发生了某些变化导致最后一行代码不起作用?像往常一样,它在我的机器上运行良好

任何建议将不胜感激。

编辑:忘记提及我已经尝试了 Web.config 中的 customErrors 模式的所有 3 种可能性(关闭、打开和 RemoteOnly)。无论此设置如何,结果都是相同的。

编辑2:我也尝试过在控制器类上使用和不使用 [HandleError] 装饰。

更新:我已经弄清楚并修复了 404。 GoDaddy 的主机控制中心的“设置”面板中有一个部分,可以在其中控制 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);
        }
    }
}

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

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

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

有人对可能有帮助的代码改进有任何建议吗?向 HttpResponse 添加不同的标头会有帮助吗?还是 GoDaddy 正在大力劫持 503?

I'm having an issue with custom errors on an ASP.NET MVC app I've deployed on my shared host. I've created an ErrorController and added the following code to Global.asax to catch unhandled exceptions, log them, and then transfer control to the ErrorController to display custom errors. This code is taken from here:

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

Application_Error is definitely firing because the logging works fine, but instead of displaying my custom error pages, I get the Go Daddy generic ones. From the title of the blog post the above code is taken from, I notice that it uses Release Candidate 2 of the MVC framework. Did something change in 1.0 that makes the last line of code not work? As usual it works great on my machine.

Any suggestions will be greatly appreciated.

Edit: Forgot to mention that I've tried all 3 possiblities for the customErrors mode in Web.config (Off, On, and RemoteOnly). Same results regardless of this setting.

Edit 2: And I've also tried it with and without the [HandleError] decoration on the Controller classes.

Update: I've figured out and fixed the 404s. There is a section of the Settings panel in Go Daddy's Hosting Control Center where 404 behavior can be controlled and the default is to show their generic page, and apparently this overrides any Web.config settings. So my custom 404 page is now showing as intended. However, 500s and 503s are still not working. I've got code in the HomeController to grab a static text version of the content if Sql Server throws an exception as follows:

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

Here's the code that attempts to fetch the static content:

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

I've verified that if I change the connection string to generate a SqlException, the code properly logs the error and then grabs and displays the static content. But if I also change the path to the static text file in Web.config to test the 503 version of the Home View, what I get instead is a page with nothing other than "service unavailable". That's it. No custom 503 message with the look and feel of the site.

Does anyone have any suggestions on improvements to the code that might help? Would it help to add different headers to the HttpResponse? Or is Go Daddy heavy-handedly hijacking the 503s?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

三生池水覆流年 2024-08-17 04:03:17

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

public bool TrySkipIisCustomErrors { get; set; }

这将我引导到最近的搜索引擎,该引擎显示了 Rick Strahl 的精彩博客文章 以及 angrypets.com 以及

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

此行为似乎还特定于集成模式下的 IIS7。来自 msdn

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

因此,基本上我最终要做的就是在将 Response.StatusCode 设置为 500 或 503 的任何代码之后添加 Response.TrySkipIisCustomErrors = true; ,现在一切功能都如下设计的。

I've found the solution and it's incredibly simple. Turns out the problem was actually in IIS7. While debugging this issue in Visual Studio I saw a property of the HttpResponse object that I hadn't noticed before:

public bool TrySkipIisCustomErrors { get; set; }

This lead me to my nearest search engine which turned up a great blog post by Rick Strahl and another on angrypets.com as well as this question here on SO. These links explain the gory details much better than I can, but this quote from Rick's post captures it pretty well:

The real confusion here occurs because the error is trapped by
ASP.NET, but then ultimately still handled by IIS which looks at the
500 status code and returns the stock IIS error page.

It also seems this behavior is specific to IIS7 in Integrated mode. From msdn:

When running in Classic mode in IIS 7.0 the TrySkipIisCustomErrors
property default value is true. When running in Integrated mode, the
TrySkipIisCustomErrors property default value is false.

So essentially all I ended up having to do is add Response.TrySkipIisCustomErrors = true; right after any code that sets the Response.StatusCode to 500 or 503 and everything now functions as designed.

短暂陪伴 2024-08-17 04:03:17

我在 GoDaddy 上托管了一个 ASP.NET MVC 网站,并且还遇到了处理自定义错误页面的问题。通过反复试验,我发现 GoDaddy 会在 HTTP 级别拦截错误。

例如,任何返回 HTTP 状态代码 404 的页面都会导致 GoDaddy 的自定义错误页面接管。最终我更改了自定义错误页面以返回 200 状态,并且与 404 相关的问题消失了。我的 HTML 是相同的,只是 HTTP 状态需要更改。

诚然,我从未尝试过对 503 状态响应执行相同的操作,但相同的缓解措施可能会起作用。如果您从返回 503 状态更改为返回 200 状态,问题会消失吗?

请注意,如果您执行此解决方法,您将希望阻止搜索引擎对您的错误页面建立索引,一旦返回 200 状态,将与常规页面无法区分(从搜索引擎的角度来看)。因此,请确保添加 META ROBOTS 标记以防止对您的错误页面建立索引,例如

<META NAME="ROBOTS" CONTENT="NOINDEX">

这种方法的缺点可能是您的页面可能会从 Google 中删除,这绝对不是一件好事!

更新:因此,此外,您还可以检测用户代理是否是爬虫,如果是爬虫则返回 503,如果不是爬虫则返回 200。参见这篇博文了解有关如何检测爬网程序的信息。是的,我知道向爬虫和用户返回不同的内容是 SEO 的禁忌,但我已经在几个网站上这样做了,到目前为止没有产生不良影响,所以我不确定这有多大的问题。

采取这两种方法(META ROBOTS 和机器人检测)可能是您最好的选择,以防任何奇怪的爬虫溜过机器人检测器。

I host an ASP.NET MVC site on GoDaddy and also faced issues dealing with custom error pages. What I found, through trial and error, was that GoDaddy intercepts errors at the HTTP level.

For example, any page which returned an HTTP status code of 404 caused GoDaddy's custom error page to take over. Eventually I changed my custom error pages to return 200 status and the 404-related problem went away. My HTML was the same, just the HTTP status needed to change.

I admittedly never tried doing the same with 503 status responses, but it's possible that the same mitigation may work. If you change from returning a 503 status to returning 200 status, does the problem go away?

Note that, if you do this workaround, you'll want to prevent search engines from indexing your error pages, which once then return a 200 status will be indistinguishable (from the search engine's perspective) from a regular page. So make sure to add a META ROBOTS tag to prevent indexing of your error pages, e.g.

<META NAME="ROBOTS" CONTENT="NOINDEX">

The downside of this approach may be that your page might be removed from Google, which is definitely not a good thing!

UPDATE: So, in addition, you can also detect whether the user agent is a crawler or not, and if it's a crawler return a 503 while if it's not a crawler, return a 200. See this blog post for info about how to detect crawlers. Yes, I know that returning different content to crawlers vs. users is an SEO no-no, but I've done this on several sites with no ill effect so far, so I'm not sure how much of a problem that is.

Doing both approaches (META ROBOTS and bot detection) may be your best bet, in case any oddball crawlers slip through the bot detector.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文