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

112

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 fonctionne pour 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 nul

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

clou
la source
S'applique également sur ASP.NET 4.0 sur Win7 avec Cassini
Marcel
ajouter "<customErrors mode =" RemoteOnly "defaultRedirect =" ~ / errors / GeneralError.aspx "redirectMode =" ResponseRewrite "/>" comme réponse confirmée
elle0087

Réponses:

137

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

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

Nous pouvons donc modifier customErrorspour ajouter ce paramètre:

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

le ResponseRewritemode nous permet de charger la «Page d'erreur» sans rediriger le navigateur, donc l'URL reste la même, et surtout pour moi, les informations d'exception ne sont pas perdues.

clou
la source
4
Cela n'a pas fonctionné pour moi. Les informations d'exception sont perdues. Je voudrais le stocker dans la session dans Application_Error () et le retirer dans le gestionnaire de Page Load () de ma page d'erreur.
BrianK
2
Cela devrait être la norme dans toute la documentation. C'est tellement bon que je ne vois plus aucune raison de soutenir l'ancien comportement. Tant que le code d'état est correct, il ne devrait pas y avoir de problème pour laisser l'URL de la demande d'origine intacte (sans effectuer de redirection du navigateur). En fait, c'est plus correct selon HTTP car le code de réponse se rapporte à l'URL demandée, pas à une demande de page d'erreur partagée. Merci pour le pointeur, j'ai manqué cette nouvelle fonctionnalité!
Tony Wall
Cela ne fonctionne pas avec les exceptions déclenchées par des contrôles à l'intérieur de UpdatePanels; la page d'erreur ne sera plus affichée.
Sam
2
puisque c'est une vieille réponse ajoutant mon commentaire pour prouver que cette valeur de ResponseRewrite de Nice one fonctionne dans Asp.Net 4.5
Sundara Prabu
38

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

avec ce schéma très illustratif:

diagramme
(source: microsoft.com )

en substance, pour obtenir ces détails d'exception, je dois les stocker moi-même dans Global.asax, pour une récupération ultérieure sur ma page d'erreur personnalisée.

il semble que le meilleur moyen soit de faire l'essentiel du travail dans Global.asax, les pages d'erreur personnalisées gérant le contenu utile plutôt que la logique.

clou
la source
18

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 tout ce dont vous avez besoin à l'exception que vous avez stocké 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 ...
}
rlb.usa
la source
35
Si vous le stockez dans l'application, qu'en est-il de tous les autres utilisateurs du système. Cela ne devrait-il pas être dans la session?
BrianK
11
en effet, c'est une très mauvaise approche de stocker ceci sur l'application ["TheException"]
Junior Mayhé
4
De plus, si vous souhaitez prendre en charge plusieurs «onglets» par utilisateur, vous souhaiterez peut-être attribuer à l'exception une clé unique dans le magasin de session, puis inclure cette clé en tant que paramètre de chaîne de requête lors de la redirection vers la page d'erreur.
Anders Fjeldstad
5
+1 Mais sachez que Application[]c'est un objet global. Théoriquement, vous pourriez avoir une condition de concurrence où une deuxième page écrase l'erreur. Cependant, comme Session[]n'est pas toujours disponible dans des conditions d'erreur, je pense que c'est le meilleur choix.
Andomar
3
Ajoutez simplement un nouveau préfixe GUID à la clé utilisée pour stocker l'exception et passez le GUID en tant que paramètre à la page d'erreur personnalisée.
SteveGSD
6

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

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

Server.Transfer() semble garder l'exception en suspens.


la source
C'est ainsi que mon application l'a fait, et cela a très bien fonctionné pour 99% des erreurs. Mais aujourd'hui, je suis tombé sur une exception qui se produit lors de l'étape de rendu. Si Server.Transferune page est à moitié rendue, le code HTML de la page vers laquelle vous transférez est simplement concaténé à ce qui a déjà été rendu. Vous pouvez donc vous retrouver avec une demi-page cassée suivie de la page d'erreur en dessous.
Kevin
Pour une raison quelconque, l'appel à Server.Transfer () provoque des problèmes et l'erreur ne s'affiche pas du tout. Et par conséquent, je ne recommande pas d'utiliser cette méthode. Utilisez simplement la ligne web.config comme suggéré ci-dessus (<customErrors mode = "RemoteOnly" defaultRedirect = "~ / errors / GeneralError.aspx" redirectMode = "ResponseRewrite" />) et cela fonctionne bien
Naresh Mittal
5

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 système sur les pages d'erreur (ce que je suppose que vous voulez faire). Vous pouvez par inadvertance révéler 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 lorsqu'une erreur se produit. Ces informations ne doivent pas être présentées à un utilisateur final.

Phil
la source
1
Dans mon cas, je ne voulais que les informations d'exception pour une utilisation back-end, mais c'est un bon conseil.
nailitdown
2
Ne répond pas à la question.
Arne Evertsson
5

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;
        }
    }
user825345
la source
4

Une considération importante que je pense que tout le monde manque ici est un scénario d'équilibrage de charge (ferme Web). Étant donné que le serveur qui exécute global.asax peut être différent du serveur qui exécute la page d'erreur personnalisée, le stockage de l'objet d'exception dans Application n'est pas fiable.

Je suis toujours à la recherche d'une solution fiable à ce problème dans une configuration de batterie de serveurs Web, et / ou une bonne explication de MS expliquant pourquoi vous ne pouvez tout simplement pas récupérer l'exception avec Server.GetLastError sur la page d'erreur personnalisée comme vous pouvez dans global.asax Application_Error.

PS Il n'est pas sûr de stocker des données dans la collection Application sans d'abord la verrouiller puis la déverrouiller.

Leonard Lobel
la source
Ce ne serait le cas que si vous effectuez une redirection côté client. Lors d'un transfert de serveur, tout cela fait partie d'une seule requête, donc application_error -> page_load se produira tous sur le serveur de la batterie, dans l'ordre.
davewasthere
2

Ceci lié à ces 2 sujets ci-dessous, je veux obtenir à la fois GetHtmlErrorMessage et Session sur la page d'erreur.

La session est nulle après ResponseRewrite

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

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

Premièrement: supprimez ResponseRewrite dans web.config

Web.config

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

Puis 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

Bao
la source
2

Cela a fonctionné pour moi. dans MVC 5


dans ~\Global.asax

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


dans ~\ControllersCréerErrorController.cs

using System.Web.Mvc;

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


dans ~\ModelsCréerFunctionTools.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 ~\ViewsCréer un dossier Error et dans ~\Views\ErrorCréerError.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/Error ouvrir la page sans erreur



Et si une erreur survient erreur se produit

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


Source: Microsoft ASP.Net

MRT2017
la source
1

Je pense que vous avez quelques options ici.

vous pouvez stocker la dernière exception dans la session et la récupérer à partir de 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 ce dernier, vous voulez vous assurer que vous utilisez la méthode Server.Transfer.

Victor
la source