web-dev-qa-db-fra.com

Page d'erreur personnalisée ASP.NET - Server.GetLastError () est null

J'ai une page d'erreur personnalisée configurée pour mon application:

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

Dans Global.asax, Application_Error (), le code suivant permet d'obtenir les détails de l'exception:

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

Au moment où j'arrive à ma page d'erreur (~/errors/GeneralError.aspx.cs), Server.GetLastError () est null

Existe-t-il un moyen d'obtenir les détails de l'exception sur la page d'erreur plutôt que dans Global.asax.cs?

ASP.NET 3.5 sur Vista/IIS7

110
nailitdown

En regardant de plus près ma configuration web.config, l'un des commentaires de ce post est très utile

dans asp.net 3.5 sp1 il y a un nouveau paramètre redirectMode

Nous pouvons donc modifier customErrors pour ajouter ce paramètre:

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

le mode ResponseRewrite nous permet de charger la "page d'erreur" sans rediriger le navigateur, de sorte que l'URL reste la même et, ce qui est important pour moi, les informations sur les exceptions ne sont pas perdues.

136
nailitdown

OK, j'ai trouvé ce message: http://msdn.Microsoft.com/en-us/library/aa479319.aspx

avec ce schéma très illustratif:

diagram
(source: Microsoft.com )

en gros, pour obtenir ces détails sur les exceptions, j'ai besoin de les stocker moi-même dans Global.asax, pour pouvoir les récupérer plus tard sur ma page d'erreur personnalisée.

il semble que le meilleur moyen consiste à effectuer la majeure partie du travail dans Global.asax, les pages d'erreur personnalisées gérant du contenu utile plutôt que la logique.

36
nailitdown

Une combinaison de ce que NailItDown et Victor ont dit. La méthode préférée/la plus simple consiste à utiliser votre Global.Asax pour stocker l'erreur, puis à la rediriger vers votre page d'erreur personnalisée.

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
}

De plus, vous devez configurer votre web.config:

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

Et enfin, faites ce que vous devez avec l'exception que vous avez stockée dans votre page d'erreur:

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 ...
}
18
rlb.usa

Essayez d'utiliser quelque chose comme Server.Transfer("~/ErrorPage.aspx"); à partir de la méthode Application_Error() de global.asax.cs.

Ensuite, à partir de Page_Load() de ErrorPage.aspx.cs, vous devriez pouvoir faire quelque chose comme: Exception exception = Server.GetLastError().GetBaseException();

Server.Transfer() semble garder l'exception à la traîne.

6
Ross Oliver

Voici ma solution ..

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

Dans 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;
        }
    }
5
user825345

Bien qu'il y ait plusieurs bonnes réponses ici, je dois souligner que ce n'est pas une bonne pratique d'afficher les messages d'exception du système sur les pages d'erreur (c'est ce que je suppose que vous voulez faire). Vous pouvez révéler par inadvertance des choses que vous ne souhaitez pas faire à des utilisateurs malveillants. Par exemple, les messages d'exception de Sql Server sont très détaillés et peuvent donner le nom d'utilisateur, le mot de passe et les informations de schéma de la base de données en cas d'erreur. Ces informations ne doivent pas être affichées pour un utilisateur final.

5
Phil

Un facteur important qui, à mon avis, manque à tout le monde ici est le scénario d'équilibrage de charge (batterie de serveurs Web). Étant donné que le serveur qui exécute global.asax peut être différent de celui exécutant la page d'erreur personnalisée, le stockage de l'objet exception dans Application n'est pas fiable.

Je cherche toujours une solution fiable à ce problème dans une configuration de batterie de serveurs Web et/ou une bonne explication de la part de MS indiquant pourquoi vous ne pouvez pas capturer l'exception avec Server.GetLastError sur la page d'erreur personnalisée, comme vous le pouvez. dans global.asax Application_Error.

P.S. Il est dangereux de stocker des données dans la collection d'applications sans d'abord les verrouiller, puis les déverrouiller.

4
Leonard Lobel

Ceci concerne les 2 sujets ci-dessous, je veux obtenir à la fois GetHtmlErrorMessage et Session on Error.

La session est nulle après ResponseRewrite

Pourquoi HttpContext.Session est-il null lorsque redirectMode = ResponseRewrite

J'ai essayé et voir une solution qui n'a pas besoin de Server.Transfer() or Response.Redirect()

Premièrement: supprimez ResponseRewrite dans web.config

Web.config

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

Alors Global.asax

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

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

Pour les références

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

2
Bao

Cela a fonctionné pour moi. dans MVC 5


dans ~\Global.asax

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


dans ~\Controllers Créer ErrorController.cs

using System.Web.Mvc;

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


dans ~\Models Créer 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;
        }
    }
}


dans ~\Views Créer un dossier Error et dans ~\Views\Error Créer 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>
    }
}


Si vous entrez cette adresse localhost/Erroropen page Whithout Error



Et si une erreur se produit error occurs

Comme cela peut être au lieu d'afficher des erreurs, la variable 'log' à stocker dans la base de données


Source: Microsoft ASP.Net

2
M.R.T2017

Je pense que vous avez deux options ici.

vous pouvez stocker la dernière exception dans la session et la récupérer depuis votre page d'erreur personnalisée; ou vous pouvez simplement rediriger vers votre page d'erreur personnalisée dans l'événement Application_error. Si vous choisissez cette dernière option, vous voulez vous assurer que vous utilisez la méthode Server.Transfer.

1
Victor