Página de erro personalizada ASP.NET - Server.GetLastError () é nulo

112

Tenho uma página de erro personalizada configurada para meu aplicativo:

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

Em Global.asax, Application_Error (), o código a seguir funciona para obter os detalhes da exceção:

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

Quando chego à minha página de erro (~ / errors / GeneralError.aspx.cs), Server.GetLastError () é nulo

Existe alguma maneira de obter os detalhes da exceção na página de erro, em vez de em Global.asax.cs?

ASP.NET 3.5 no Vista / IIS7

Nailitdown
fonte
Aplica-se também no ASP.NET 4.0 no Win7 com Cassini
Marcel
adicionar "<customErrors mode =" RemoteOnly "defaultRedirect =" ~ / errors / GeneralError.aspx "redirectMode =" ResponseRewrite "/>" como resposta confirmada
elle0087

Respostas:

137

Olhando mais de perto a configuração do meu web.config, um dos comentários nesta postagem é muito útil

no asp.net 3.5 sp1, há um novo parâmetro redirectMode

Portanto, podemos alterar customErrorspara adicionar este parâmetro:

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

a ResponseRewrite modo nos permite carregar a «página de erro» sem redirecionar o navegador, de modo que a URL permanece a mesma e, o que é importante para mim, as informações de exceção não são perdidas.

Nailitdown
fonte
4
Isso não funcionou para mim. As informações da exceção são perdidas. Gostaria de armazená-lo na sessão em Application_Error () e puxá-lo de volta no manipulador Page_Load () da minha página de erro.
BrianK
2
Essa deve ser a norma em toda a documentação. Isso é tão bom que não vejo mais razão para apoiar o antigo comportamento. Contanto que o código de status esteja correto, não deve haver nenhum problema em deixar o URL de solicitação original intacto (sem fazer um redirecionamento do navegador). Na verdade, isso é mais correto de acordo com o HTTP porque o código de resposta está relacionado ao URL solicitado, não a uma solicitação de página de erro compartilhada. Obrigado pela indicação de que perdi esse novo recurso!
Tony Wall
Isso não funciona com exceções acionadas por controles dentro de UpdatePanels; a página de erro não será mais exibida.
Sam
2
já que é uma resposta antiga adicionando meu comentário para provar este Nice one ResponseRewrite valor em redirectmode funciona no Asp.Net 4.5
Sundara Prabu
38

OK, encontrei esta postagem: http://msdn.microsoft.com/en-us/library/aa479319.aspx

com este diagrama muito ilustrativo:

diagrama
(fonte: microsoft.com )

em essência, para obter esses detalhes de exceção, eu mesmo preciso armazená-los em Global.asax, para recuperação posterior em minha página de erro personalizada.

parece que a melhor maneira é fazer a maior parte do trabalho em Global.asax, com as páginas de erro personalizadas lidando com conteúdo útil em vez de lógica.

Nailitdown
fonte
18

Uma combinação do que NailItDown e Victor disseram. A maneira preferida / mais fácil é usar seu Global.Asax para armazenar o erro e, em seguida, redirecionar para sua página de erro personalizada.

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
}

Além disso, você precisa configurar seu web.config :

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

E, finalmente, faça o que for necessário, exceto que você armazenou em sua página de erro :

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 ...
}
rlb.usa
fonte
35
Se você armazená-lo no aplicativo, que tal todos os outros usuários do sistema. Não deveria ser na sessão?
BrianK
11
na verdade, é uma abordagem muito ruim armazenar isso no aplicativo ["TheException"]
Junior Mayhé
4
Além disso, se você deseja oferecer suporte a várias "guias" por usuário, pode fornecer à exceção uma chave exclusiva no armazenamento de sessão e, em seguida, incluir essa chave como um parâmetro de querystring ao redirecionar para a página de erro.
Anders Fjeldstad de
5
+1 Mas esteja ciente de que Application[]é um objeto global. Teoricamente, você pode ter uma condição de corrida em que uma segunda página substitui o erro. No entanto, como Session[]nem sempre está disponível em condições de erro, acho que essa é a melhor escolha.
Andomar
3
Basta adicionar um novo prefixo GUID à chave usada para armazenar a exceção e passar o GUID como um parâmetro para a página de erro personalizada.
SteveGSD
6

Tente usar algo como Server.Transfer("~/ErrorPage.aspx");no Application_Error()método global.asax.cs

Então, Page_Load()de ErrorPage.aspx.cs, você deve estar bem para fazer algo como:Exception exception = Server.GetLastError().GetBaseException();

Server.Transfer() parece manter a exceção por aí.


fonte
É assim que meu aplicativo fez isso, e funcionou muito bem para 99% dos erros. Mas hoje me deparei com uma exceção que ocorre durante a etapa de renderização. Se você Server.Transferdepois que uma página é renderizada pela metade, o HTML da página para a qual você transfere é simplesmente concatenado com o que já foi renderizado. Assim, você pode acabar com meia página quebrada seguida pela página de erro abaixo dela.
Kevin
Por algum motivo, a chamada para Server.Transfer () causa problemas e o erro não é exibido. E, portanto, eu não recomendo usar esse método. Basta usar a linha web.config conforme sugerido acima (<customErrors mode = "RemoteOnly" defaultRedirect = "~ / errors / GeneralError.aspx" redirectMode = "ResponseRewrite" />) e funcionará bem
Naresh Mittal
5

Embora existam várias respostas boas aqui, devo salientar que não é uma boa prática exibir mensagens de exceção do sistema nas páginas de erro (que é o que estou supondo que você deseja fazer). Você pode inadvertidamente revelar coisas que não deseja para usuários mal-intencionados. Por exemplo, as mensagens de exceção do Sql Server são muito detalhadas e podem fornecer o nome de usuário, a senha e as informações de esquema do banco de dados quando ocorre um erro. Essas informações não devem ser exibidas para um usuário final.

Phil
fonte
1
No meu caso, eu só queria as informações de exceção para uso de back end, mas esse é um bom conselho.
Nailitdown em
2
Não responde à pergunta.
Arne Evertsson
5

Aqui está minha solução ..

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

Em 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;
        }
    }
user825345
fonte
4

Uma consideração importante que acho que todos estão faltando aqui é um cenário de balanceamento de carga (web farm). Uma vez que o servidor que está executando global.asax pode ser diferente do servidor que está executando a página de erro personalizada, armazenar o objeto de exceção em Aplicativo não é confiável.

Ainda estou procurando uma solução confiável para este problema em uma configuração de web farm e / ou uma boa explicação do MS sobre por que você simplesmente não pode pegar a exceção com Server.GetLastError na página de erro personalizada como você pode em global.asax Application_Error.

PS Não é seguro armazenar dados na coleção de aplicativos sem primeiro bloqueá-los e depois desbloqueá-los.

Leonard Lobel
fonte
Isso só aconteceria se você estivesse fazendo um redirecionamento do lado do cliente. Ao fazer uma transferência de servidor, é tudo parte de uma solicitação, portanto, application_error -> page_load acontecerá em um servidor no farm, em sequência.
davewasthere
2

Isso está relacionado a esses 2 tópicos abaixo, quero obter a página GetHtmlErrorMessage e Sessão na página de erro.

A sessão é nula após ResponseRewrite

Por que HttpContext.Session é nulo quando redirectMode = ResponseRewrite

Eu tentei e vi uma solução que não precisa Server.Transfer() or Response.Redirect()

Primeiro: remova ResponseRewrite em web.config

Web.config

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

Então Global.asax

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

Em seguida, 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
            }

Para referências

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

Bao
fonte
2

Funcionou para mim em MVC 5


no ~\Global.asax

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


em ~\ControllerscriarErrorController.cs

using System.Web.Mvc;

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


em ~\ModelscriarFunctionTools.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;
        }
    }
}


em ~\ViewsCriar Pasta Error e em ~\Views\ErrorCriarError.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>
    }
}


Se você inserir este endereço localhost/Error abrir página sem erro



E se ocorrer um erro ocorreu um erro

Como pode ser, em vez de exibir erros, a variável 'log' deve ser armazenada no banco de dados


Fonte: Microsoft ASP.Net

MRT2017
fonte
1

Acho que você tem algumas opções aqui.

você pode armazenar a última exceção na sessão e recuperá-la de sua página de erro personalizada; ou você pode apenas redirecionar para sua página de erro personalizada dentro do evento Application_error. Se você escolher a última opção, certifique-se de usar o método Server.Transfer.

Vencedor
fonte