ASP.NET自定义错误页面-Server.GetLastError()为null


112

我为我的应用程序设置了一个自定义错误页面:

<customErrors mode="On" defaultRedirect="~/errors/GeneralError.aspx"
/>

在Global.asax的Application_Error()中,以下代码用于获取异常详细信息:

  Exception ex = Server.GetLastError();
  if (ex != null)
    {
        if (ex.GetBaseException() != null)
            ex = ex.GetBaseException();
    }

到错误页面(〜/ errors / GeneralError.aspx.cs)时,Server.GetLastError()为null

有什么办法可以在错误页面上而不是在Global.asax.cs中获得异常详细信息?

Vista / IIS7上的ASP.NET 3.5


适用于Win7上带有Cassini的ASP.NET 4.0
Marcel

添加“ <customErrors mode =“ RemoteOnly” defaultRedirect =“〜/ errors / GeneralError.aspx” redirectMode =“ ResponseRewrite” />“作为已确认的答案
elle0087

Answers:


137

仔细查看我的web.config设置,这篇文章中的评论之一非常有帮助

在asp.net 3.5 sp1中,有一个新参数redirectMode

因此,我们可以修改customErrors以添加以下参数:

<customErrors mode="RemoteOnly" defaultRedirect="~/errors/GeneralError.aspx" redirectMode="ResponseRewrite" />

ResponseRewrite模式允许我们在不重定向浏览器的情况下加载“错误页面”,因此URL保持不变,并且对我而言重要的是,异常信息不会丢失。


4
这对我不起作用。异常信息丢失。我将其存储在Application_Error()的会话中,然后将其拉回到错误页面的Page_Load()处理程序中。
BrianK

2
那应该是所有文档中的规范。太好了,我认为没有理由再支持旧的行为了。只要状态码正确,就可以保持原始请求URL不变(不进行浏览器重定向)。实际上,根据HTTP,这是更正确的,因为响应代码与请求的URL有关,而不是与共享错误页面请求有关。感谢您提供的指针,我错过了该新功能!
托尼·沃尔

这不适用于UpdatePanels 内部的控件触发的异常;错误页面将不再显示。
山姆

2
因为它的答案很旧,所以添加了我的评论来证明重定向模式中的尼斯一个ResponseRewrite值可在Asp.Net 4.5中使用
Sundara Prabu,2016年

38

好的,我找到了这篇文章:http : //msdn.microsoft.com/en-us/library/aa479319.aspx

这个非常说明性的图表:

图
(来源:microsoft.com

本质上,要获取这些异常详细信息,我需要将它们自己存储在Global.asax中,以便以后在我的自定义错误页面上进行检索。

似乎最好的方法是在Global.asax中完成大部分工作,其中自定义错误页面处理有用的内容而不是逻辑。


18

NailItDown和Victor说的结合在一起。首选/最简单的方法是使用Global.Asax存储错误,然后重定向到自定义错误页面。

Global.asax

    void Application_Error(object sender, EventArgs e) 
{
    // Code that runs when an unhandled error occurs
    Exception ex = Server.GetLastError();
    Application["TheException"] = ex; //store the error for later
    Server.ClearError(); //clear the error so we can continue onwards
    Response.Redirect("~/myErrorPage.aspx"); //direct user to error page
}

另外,您需要设置您的web.config

  <system.web>
    <customErrors mode="RemoteOnly" defaultRedirect="~/myErrorPage.aspx">
    </customErrors>
  </system.web>

最后,除了存储在错误页面中的异常外,您还可以执行其他操作:

protected void Page_Load(object sender, EventArgs e)
{

    // ... do stuff ...
    //we caught an exception in our Global.asax, do stuff with it.
    Exception caughtException = (Exception)Application["TheException"];
    //... do stuff ...
}

35
如果将其存储在应用程序中,那么系统的所有其他用户呢?不应该在会议中吗?
BrianK

11
实际上,将其存储在Application [“ TheException”]中确实是一种非常糟糕的方法
JuniorMayhé2010年

4
另外,如果要为每个用户支持多个“选项卡”,则可能需要为异常提供会话存储中的唯一键,然后在重定向到错误页面时将该键作为querystring参数包含在内。
2011年

5
+1但请注意,这Application[]是一个全局对象。从理论上讲,您可能会遇到竞争情况,其中第二页会覆盖该错误。但是,由于Session[]在错误情况下并不总是可用,因此我认为这是更好的选择。
Andomar 2013年

3
只需在用于存储异常的键上添加一个新的GUID前缀,然后将GUID作为参数传递给自定义错误页面即可。
SteveGSD,2015年

6

尝试Server.Transfer("~/ErrorPage.aspx");Application_Error()global.asax.cs方法中使用类似的方法

然后从Page_Load()ErrorPage.aspx.cs 内部,您应该可以做类似的事情:Exception exception = Server.GetLastError().GetBaseException();

Server.Transfer() 似乎让异常情况徘徊。


这就是我的应用程序的工作方式,它对于99%的错误都非常有效。但是今天我遇到了一个在渲染步骤中发生的异常。如果您Server.Transfer在页面被半渲染之后,则将您传输到的页面的HTML仅连接到已经渲染的任何内容上。因此,您可能会遇到一半的损坏页面,其后是错误页面。
凯文

由于某些原因,对Server.Transfer()的调用会导致问题,并且根本不会显示错误。因此,我不建议使用此方法。简单地使用的web.config线以上(通过<customErrors模式= “仅限远程”的defaultRedirect = “〜/错误/ GeneralError.aspx” redirectMode = “ResponseRewrite”/>)的建议,它工作正常
纳雷什塔尔

5

尽管这里有几个不错的答案,但我必须指出,在错误页面上显示系统异常消息(这是我假设要执行的操作)不是一种好习惯。您可能无意间向恶意用户透露了您不希望这样做的内容。例如,Sql Server异常消息非常冗长,并且在发生错误时可以提供数据库的用户名,密码和架构信息。该信息不应显示给最终用户。


1
就我而言,我只想将异常信息供后端使用,但这是一个很好的建议。
nailitdown 2011年

2
不回答问题。
Arne Evertsson

5

这是我的解决方案。

在Global.aspx中:

void Application_Error(object sender, EventArgs e)
    {
        // Code that runs when an unhandled error occurs

        //direct user to error page 
        Server.Transfer("~/ErrorPages/Oops.aspx"); 
    }

在Oops.aspx中:

protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
            LoadError(Server.GetLastError()); 
    }

    protected void LoadError(Exception objError)
    {
        if (objError != null)
        {
            StringBuilder lasterror = new StringBuilder();

            if (objError.Message != null)
            {
                lasterror.AppendLine("Message:");
                lasterror.AppendLine(objError.Message);
                lasterror.AppendLine();
            }

            if (objError.InnerException != null)
            {
                lasterror.AppendLine("InnerException:");
                lasterror.AppendLine(objError.InnerException.ToString());
                lasterror.AppendLine();
            }

            if (objError.Source != null)
            {
                lasterror.AppendLine("Source:");
                lasterror.AppendLine(objError.Source);
                lasterror.AppendLine();
            }

            if (objError.StackTrace != null)
            {
                lasterror.AppendLine("StackTrace:");
                lasterror.AppendLine(objError.StackTrace);
                lasterror.AppendLine();
            }

            ViewState.Add("LastError", lasterror.ToString());
        }
    }

   protected void btnReportError_Click(object sender, EventArgs e)
    {
        SendEmail();
    }

    public void SendEmail()
    {
        try
        {
            MailMessage msg = new MailMessage("webteam", "webteam");
            StringBuilder body = new StringBuilder();

            body.AppendLine("An unexcepted error has occurred.");
            body.AppendLine();

            body.AppendLine(ViewState["LastError"].ToString());

            msg.Subject = "Error";
            msg.Body = body.ToString();
            msg.IsBodyHtml = false;

            SmtpClient smtp = new SmtpClient("exchangeserver");
            smtp.Send(msg);
        }

        catch (Exception ex)
        {
            lblException.Text = ex.Message;
        }
    }

4

我认为每个人都缺少的一个重要考虑因素是负载平衡(Web场)方案。由于执行global.asax的服务器可能与执行自定义错误页面的服务器不同,因此将异常对象存储在Application中是不可靠的。

我仍在寻找Web场配置中此问题的可靠解决方案,和/或MS很好地解释了为什么您不能像在可以的情况下那样在自定义错误页面上使用Server.GetLastError拾取异常。在global.asax Application_Error中。

PS在不先锁定数据然后将其解锁的​​情况下将数据存储在Application集合中是不安全的。


仅当您要进行客户端重定向时才是这种情况。当进行服务器传输时,这都是一个请求的一部分,因此application_error-> page_load将全部按顺序在服务器场中的一台服务器上发生。
davewasthere

2

这与下面的两个主题有关,我想在错误页面上同时获取GetHtmlErrorMessage和Session。

在ResponseRewrite之后,会话为null

当redirectMode = ResponseRewrite时,为什么HttpContext.Session为null

我试着看到不需要的解决方案 Server.Transfer() or Response.Redirect()

首先:在web.config中删除ResponseRewrite

Web.config

<customErrors defaultRedirect="errorHandler.aspx" mode="On" />

然后是Global.asax

    void Application_Error(object sender, EventArgs e)
    {
         if(Context.IsCustomErrorEnabled)
         {     
            Exception ex = Server.GetLastError();
            Application["TheException"] = ex; //store the error for later
         }
    }

然后是errorHandler.aspx.cs

        protected void Page_Load(object sender, EventArgs e)
            {       
                string htmlErrorMessage = string.Empty ;
                Exception ex = (Exception)Application["TheException"];
                string yourSessionValue = HttpContext.Current.Session["YourSessionId"].ToString();

                //continue with ex to get htmlErrorMessage 
                if(ex.GetHtmlErrorMessage() != null){              
                    htmlErrorMessage = ex.GetHtmlErrorMessage();
                }   
                // continue your code
            }

供参考

http://www.developer.com/net/asp/article.php/3299641/ServerTransfer-Vs-ResponseRedirect.htm


2

它为我工作。在MVC 5中


~\Global.asax

void Application_Error(object sender, EventArgs e)
{
    FTools.LogException();
    Response.Redirect("/Error");
}


~\Controllers创建ErrorController.cs

using System.Web.Mvc;

namespace MVC_WebApp.Controllers
{
    public class ErrorController : Controller
    {
        // GET: Error
        public ActionResult Index()
        {
            return View("Error");
        }
    }
}


~\Models创建FunctionTools.cs

using System;
using System.Web;

namespace MVC_WebApp.Models
{
    public static class FTools
    {
        private static string _error;
        private static bool _isError;

        public static string GetLastError
        {
            get
            {
                string cashe = _error;
                HttpContext.Current.Server.ClearError();
                _error = null;
                _isError = false;
                return cashe;
            }
        }
        public static bool ThereIsError => _isError;

        public static void LogException()
        {
            Exception exc = HttpContext.Current.Server.GetLastError();
            if (exc == null) return;
            string errLog = "";
            errLog += "**********" + DateTime.Now + "**********\n";
            if (exc.InnerException != null)
            {
                errLog += "Inner Exception Type: ";
                errLog += exc.InnerException.GetType() + "\n";
                errLog += "Inner Exception: ";
                errLog += exc.InnerException.Message + "\n";
                errLog += "Inner Source: ";
                errLog += exc.InnerException.Source + "\n";
                if (exc.InnerException.StackTrace != null)
                {
                    errLog += "\nInner Stack Trace: " + "\n";
                    errLog += exc.InnerException.StackTrace + "\n";
                }
            }
            errLog += "Exception Type: ";
            errLog += exc.GetType().ToString() + "\n";
            errLog += "Exception: " + exc.Message + "\n";
            errLog += "\nStack Trace: " + "\n";
            if (exc.StackTrace != null)
            {
                errLog += exc.StackTrace + "\n";
            }
            _error = errLog;
            _isError = true;
        }
    }
}


~\Views创建文件夹Error~\Views\Error创建Error.cshtml

@using MVC_WebApp.Models
@{
    ViewBag.Title = "Error";
    if (FTools.ThereIsError == false)
    {
        if (Server.GetLastError() != null)
        {
            FTools.LogException();
        }
    }
    if (FTools.ThereIsError == false)
    {
        <br />
        <h1>No Problem!</h1>
    }
    else
    {
        string log = FTools.GetLastError;
        <div>@Html.Raw(log.Replace("\n", "<br />"))</div>
    }
}


如果您输入这个地址 localhost/Error 打开页面Whithout错误



如果发生错误 发生错误

可以代替显示错误,而是将变量“ log”存储在数据库中


资料来源:Microsoft ASP.Net


1

我认为您在这里有两个选择。

您可以将最后一个异常存储在会话中,然后从您的自定义错误页面中检索它;或者您可以直接重定向到Application_error事件中的自定义错误页面。如果选择后者,则要确保使用Server.Transfer方法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.