5

在我的 ASP.NET MVC 应用程序中,我希望有一个通用错误页面,该页面与返回的 HTTP 500 一起显示。类似于Stack Overflow 上的这个页面

当然,我希望尽可能多地重用代码,以便页面的样式与我的应用程序中的其他页面相同,因此我希望有一个 ASP.NET 页面重用母版页,而不是纯 HTML 文件。但是,如果我这样做,我有一个页面,其中包含一些服务器端可执行代码,用于创建提供给客户端的实际 HTML。在 Stack Overflow 通用错误页面的情况下,代码检查用户是否已注册、检索他的代表等,并将其元素插入 HTML。

该代码可能会失败,如果发生这种情况,页面构造不正确,服务器会引发异常。

如何优雅地处理这种情况(为错误页面构建 HTML 时出错)?典型的做法是什么?

4

2 回答 2

7

我喜欢首先对可能的错误进行分类。

对错误进行分类。

  1. 找不到页面(这是我的日志)。
  2. 通过尝试破解页面来破坏页面的参数
  3. 错误的用户数据输入错误。
  4. 完全未知的错误 - 我们必须修复的新错误。
  5. 一个非常严重的一般错误 - 没有运行 - 例如,数据库根本没有打开。

我们的目标

现在显示错误页面,但仅在极少数情况下。

因此,当用户在没有页面的情况下进行操作时,我们会尝试捕获用户的所有错误输入,并向他展示如何在没有任何错误的情况下继续操作。

全局错误处理程序

global.asax您可以从使用 void捕获所有错误开始Application_Error(object sender, EventArgs e)

void Application_Error(object sender, EventArgs e) 
{
    try
    {
        Exception LastOneError = Server.GetLastError();

        if (LastOneError != null)
        {
            Debug.Fail("Unhandled error: " + LastOneError.ToString());

            if (!(EventLog.SourceExists(SourceName)))
                EventLog.CreateEventSource(SourceName, LogName);

            EventLog MyLog = new EventLog();
            MyLog.Source = SourceName;
            StringBuilder cReportMe = new StringBuilder();

            cReportMe.Append("[Error come from ip:");
            cReportMe.Append(GetRemoteHostIP());
            cReportMe.Append("] ");
            cReportMe.Append("Last Error:");
            cReportMe.Append(LastOneError.ToString());

            if (LastOneError.ToString().Contains("does not exist."))
            {
                // page not found
                MyLog.WriteEntry(cReportMe.ToString(), EventLogEntryType.Warning, 998);
            }
            else
            {
                MyLog.WriteEntry(cReportMe.ToString(), EventLogEntryType.Error, 999);
            }               
        }
    }
    catch (Exception ex)
    {
        Debug.Fail("Unhandled error: " + GlobalFun.GetErrorMessage(ex));
    }

    string cTheFile = HttpContext.Current.Request.Path;

    // to avoid close loop and stackoverflow
    if(!cTheFile.EndsWith("error.aspx"))
        Server.Transfer("~/error.aspx");
}

这个全局错误句柄有一个主要目标,告诉我什么不工作并且在到达这里之前无法处理它,所以我记录它(而不是记录它的方式)并尽快修复它。当我调试我的代码时,我没有让服务器传输能够快速定位错误。

对于黑客错误案例

在这种情况下,最好现在显示任何错误,但只需通过重定向重新加载页面。如何知道它是否试图破解页面?

如果在回发时您会收到一些您知道的参数的 CRC/hach 错误。查看答案https://stackoverflow.com/a/2551810/159270以查看视图状态错误的示例以及如何处理它。

我知道 MVC 没有视图状态,但是您的代码中可能有其他加密字符串,或者您可以知道它何时被破坏的某种安全性。这是一般的想法:

if(IsPostBack && HashErrorOnParametres)
{
  LogIt();
  Responce.Redirect(Request.RawUrl, true);
  return;
}

对于一个非常困难的一般错误

假设您的数据库根本没有打开,那么所有用户都开始从通用处理程序中看到错误页面。在那里,您可能有一个额外的选项来重新启动池,或者在过去 5 分钟内出现 20 个错误后停止页面,或者类似的事情,然后向您发送电子邮件以运行并修复这个非常困难的错误。

对于其余的软错误

我认为所有可能的已知错误必须在页面内使用 try/catch 进行处理,并向用户显示出错的消息,如果此错误来自用户,当然记录它以查看并修复它。

将页面分成几部分,也许一部分是抛出错误,其余部分工作正常,如果这不是那么重要,您可以简单地隐藏这部分,并显示其余部分,直到您修复它。例如,如果您只是显示有关产品的信息,而涉及某个部分的部分抛出错误,您可以简单地隐藏该部分并在日志中看到它并修复它。

有时我尝试使用每页处理错误,protected override void OnError(EventArgs e)但这对我没有帮助,我将其删除。我处理每个操作的错误,如果它们不是关键的,我会隐藏它们,直到我修复它们。

其余的正常错误显示在用户身上,例如未输入正确的数据...一条消息并告诉他们要修复什么。

正如我所说,我的目标是根本不显示错误页面。

于 2012-05-25T09:34:07.043 回答
1

基本上在你的 web.config 中你需要这样的东西:

<customErrors mode="RemoteOnly" defaultRedirect="~/ErrorPages/Oops.aspx">
</customErrors>

自然,您的错误页面的路径将与上述不同。

您可以更全面地了解如何处理特定错误,例如像这样的 404:

<customErrors mode="RemoteOnly" defaultRedirect="~/ErrorPages/Oops.aspx">
      <error statusCode="404" redirect="~/ErrorPages/404.aspx" />
</customErrors>

编辑:

根据评论,这就是我的意思:

当您构建“oops.aspx”页面时,我建议您保持简单;如果可能,根本没有执行代码。如果您确实需要在“oops.aspx”中执行代码,则将该代码放在一个try..catch块中并吃掉异常:

try {
    // Your oops code.
}
catch (Exception e) {
    // Do nothing with the exception.
    // Maybe plonk it in a log file, or System.Diagnostics.Trace.WriteLine(e.Message);
}

关键是您要避免调用“oops.aspx”,因为“oops.aspx”本身会导致异常!递归问题啊哈。

您不需要在母版页中尝试...catch(除非您愿意),我的意思只是集中在“oops.aspx”页面上。

于 2012-05-25T09:14:58.270 回答