Application_Error non si attiva quando customerrors = "On"


124

Ho un codice nell'evento global.asaxdel file Application_Errorche viene eseguito quando si verifica un errore e invia a me stesso i dettagli dell'errore tramite posta elettronica.

void Application_Error(object sender, EventArgs e)
{
    var error = Server.GetLastError();

    if (error.Message != "Not Found")
    {
        // Send email here...
    }

}

Funziona bene quando lo eseguo in Visual Studio, tuttavia quando pubblico sul nostro server live l' Application_Errorevento non si attiva.

Dopo alcuni test, posso ottenere l' Application_Errorattivazione quando ho impostato customErrors="Off", tuttavia impostandolo di nuovo su customErrors="On"interrompe lo scatto dell'evento.

Qualcuno può suggerire perché Application_Errornon dovrebbe sparare quando customErrorssono abilitati in web.config?


Sto avendo lo stesso identico problema. Ho anche trovato questa domanda SO: stackoverflow.com/questions/3713939/… che suggerisce di mettere il server IIS7 in modalità classica. Purtroppo non è un'opzione per noi. Qualcuno ha soluzioni migliori?
Jesse Webb

Ecco un'altra domanda correlata e le sue risposte (nessuna delle quali è accettata) suggeriscono di non usare affatto Application_Error () ... stackoverflow.com/questions/1194578/…
Jesse Webb

@ Gweebz Ho pubblicato una risposta su come ho aggirato questo problema, ma non ho ancora trovato alcuna documentazione solida sul motivo per cui stavo ottenendo questo comportamento.
WDuffy

Ho aggiunto una risposta che spiega perché il Application_Error()metodo non è stato richiamato. Ho anche spiegato la mia soluzione finale.
Jesse Webb

Consiglio vivamente questo articolo per far funzionare gli errori personalizzati.
ThomasArdal

Risposte:


133

AGGIORNAMENTO
Poiché questa risposta fornisce una soluzione, non la modificherò, ma ho trovato un modo molto più pulito per risolvere questo problema. Vedi la mia altra risposta per i dettagli ...

Risposta originale:
ho capito perché il Application_Error()metodo non viene invocato ...

Global.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute()); // this line is the culprit
    }
...
}

Per impostazione predefinita (quando viene generato un nuovo progetto), un'applicazione MVC ha una logica nel Global.asax.csfile. Questa logica viene utilizzata per mappare le rotte e registrare i filtri. Per impostazione predefinita, registra solo un filtro: un HandleErrorAttributefiltro. Quando customErrors è attivo (o tramite richieste remote quando è impostato su RemoteOnly), HandleErrorAttribute indica a MVC di cercare una visualizzazione Error e non chiama mai il Application_Error()metodo. Non sono riuscito a trovare la documentazione di questo, ma è spiegato in questa risposta su programmers.stackexchange.com .

Per ottenere il metodo ApplicationError () chiamato per ogni eccezione non gestita, rimuovere semplicemente la riga che registra il filtro HandleErrorAttribute.

Ora il problema è: come configurare customErrors per ottenere ciò che desideri ...

Il valore predefinito della sezione customErrors è redirectMode="ResponseRedirect". È possibile specificare anche l'attributo defaultRedirect come route MVC. Ho creato un ErrorController che era molto semplice e ho cambiato il mio web.config in questo modo ...

web.config

<customErrors mode="RemoteOnly" redirectMode="ResponseRedirect" defaultRedirect="~/Error">
  <error statusCode="404" redirect="~/Error/PageNotFound" />
</customErrors>

Il problema con questa soluzione è che esegue un reindirizzamento 302 agli URL di errore e quindi quelle pagine rispondono con un codice di stato 200. Questo porta a Google che indicizza le pagine di errore che è male. Inoltre non è molto conforme alle specifiche HTTP. Quello che volevo fare non era reindirizzare e sovrascrivere la risposta originale con le mie visualizzazioni di errore personalizzate.

Ho provato a cambiare redirectMode="ResponseRewrite". Sfortunatamente, questa opzione non supporta route MVC , solo pagine HTML statiche o ASPX. All'inizio ho provato a utilizzare una pagina HTML statica ma il codice di risposta era ancora 200 ma, almeno, non ha reindirizzato. Allora ho avuto un'idea da questa risposta ...

Ho deciso di rinunciare a MVC per la gestione degli errori. Ho creato un Error.aspxe un file PageNotFound.aspx. Queste pagine erano molto semplici ma avevano un pezzo di magia ...

<script type="text/C#" runat="server">
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        Response.StatusCode = (int) System.Net.HttpStatusCode.InternalServerError;
    }
</script>

Questo blocco indica alla pagina di essere servita con il codice di stato corretto. Di grossolano, nella pagina PageNotFound.aspx, ho usato HttpStatusCode.NotFoundinvece. Ho cambiato il mio web.config in questo modo ...

<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite" defaultRedirect="~/Error.aspx">
  <error statusCode="404" redirect="~/PageNotFound.aspx" />
</customErrors>

Ha funzionato tutto perfettamente!

Sommario:

  • Rimuovi la riga: filters.Add(new HandleErrorAttribute());
  • Usa il Application_Error()metodo per registrare le eccezioni
  • Usa customErrors con ResponseRewrite, che punta alle pagine ASPX
  • Rendere le pagine ASPX responsabili dei propri codici di stato della risposta

Ci sono un paio di aspetti negativi che ho notato con questa soluzione.

  • Le pagine ASPX non possono condividere alcun markup con i modelli Razor, ho dovuto riscrivere il markup standard di intestazione e piè di pagina del nostro sito Web per un aspetto coerente.
  • È possibile accedere alle pagine * .aspx direttamente premendo i relativi URL

Ci sono soluzioni per questi problemi, ma non ero abbastanza preoccupato per fare alcun lavoro extra.

Spero che questo possa aiutare tutti!


2
+1! Soluzione davvero buona, ma quali potrebbero essere le conseguenze del mescolare MVC con pagine aspx?
Diego

2
Abbiamo questo in PROD da un paio di mesi ormai e non ho trovato alcun impatto negativo. L'unico "trucco" era che dovevamo cambiare il nostro CI e distribuire per eseguire un'attività AspCompile MSBUILD perché MVC non ne aveva bisogno, ma quando abbiamo aggiunto i file .as , lo hanno richiesto. Potrebbero esserci altri problemi ma non sono ancora emersi ...
Jesse Webb

Ho provato a utilizzare questo approccio in MVC4 e apparentemente funziona solo per me quando l'ho ottenuto <customErrors mode="Off" />. L'aver filters.Add(new HandleErrorAttribute());rimosso o meno non ha alcun effetto.
Grzegorz Sławecki

nella pagina aspx puoi aggiungere una chiamata ajax per chiamare la vista che vuoi mostrare. Evita di dover duplicare il codice
Mike

71

Ho risolto questo problema creando un ExceptionFilter e registrando l'errore lì invece di Application_Error. Tutto quello che devi fare è aggiungere una chiamata a in RegisterGlobalFilters

log4netExceptionFilter.cs

using System
using System.Web.Mvc;

public class log4netExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        Exception ex = context.Exception;
        if (!(ex is HttpException)) //ignore "file not found"
        {
            //Log error here
        }
    }
}

Global.asax.cs

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new log4netExceptionFilter()); //must be before HandleErrorAttribute
    filters.Add(new HandleErrorAttribute());
}

6
+1 Se questa risposta funziona, sicuramente mi sembra il modo più elegante e forse inteso (dal framework MVC) per gestire gli errori.
Matt Hamsmith

2
Questo funziona perfettamente per me ed è molto più ordinato della risposta accettata qui. Bello!
Alex Warren

3
Questo funzionerà per risolvere il problema della registrazione delle eccezioni. Come hai combinato questo con la visualizzazione di pagine di errore personalizzate?
Jesse Webb

3
Ho provato questo e ha funzionato bene tranne nel caso dei 404. Per gli anni 404, non mostrerebbe la vista Errors.cshtml, mi darebbe solo un YSoD. Se non sono necessari 404 personalizzati, questa soluzione è decisamente più pulita!
Jesse Webb

1
Mi piace. Funziona con "CustomErrors = On"
Illidan

36

Ho trovato un articolo che descrive un modo molto più pulito di creare pagine di errore personalizzate in un'app Web MVC3 che non impedisce la possibilità di registrare le eccezioni.

La soluzione è utilizzare l' <httpErrors>elemento della <system.webServer>sezione.

Ho configurato il mio Web.config in questo modo ...

<httpErrors errorMode="DetailedLocalOnly" existingResponse="Replace">
  <remove statusCode="404" subStatusCode="-1" />
  <remove statusCode="500" subStatusCode="-1" />
  <error statusCode="404" path="/Error/NotFound" responseMode="ExecuteURL" />
  <error statusCode="500" path="/Error" responseMode="ExecuteURL" />
</httpErrors>

Ho anche configurato customErrorsper avere mode="Off"(come suggerito dall'articolo).

Ciò rende le risposte sovrascritte dalle azioni di un ErrorController. Ecco quel controller:

public class ErrorController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult NotFound()
    {
        return View();
    }
}

Le visualizzazioni sono molto semplici, ho solo usato la sintassi standard Razor per creare le pagine.

Questo da solo dovrebbe essere sufficiente per utilizzare le pagine di errore personalizzate con MVC.

Avevo anche bisogno della registrazione delle eccezioni, quindi ho rubato la soluzione di Mark di utilizzare un ExceptionFilter personalizzato ...

public class ExceptionPublisherExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext exceptionContext)
    {
        var exception = exceptionContext.Exception;
        var request = exceptionContext.HttpContext.Request;
        // log stuff
    }
}

L'ultima cosa di cui hai bisogno è registrare il filtro eccezioni nel tuo file Global.asax.cs :

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new ExceptionPublisherExceptionFilter());
    filters.Add(new HandleErrorAttribute());
}

Questa sembra una soluzione molto più pulita della mia risposta precedente e funziona altrettanto bene per quanto ne so. Mi piace soprattutto perché non mi sembrava di combattere contro il framework MVC; questa soluzione effettivamente lo sfrutta!


6
Vale la pena ricordare che penso che questa soluzione funzioni solo in IIS 7 e versioni successive; l'elemento httpErrors è stato aggiunto solo di recente.
Jesse Webb

Questa risposta non ha funzionato per me, mi ha, mi prende in giro un blu orribile: HTTP Error 500.0 - Internal Server Error The page cannot be displayed because an internal server error has occurred. schermata di errore, non la mia /Error/Indexpagina richiesta
Serj Sagan

@ SerjSagan L'ho appena provato in un nuovo progetto per testare i passaggi e funziona bene. Stai usando IIS, IIS Express o VS Dev Server? Questo non funzionerà in VS Dev Server. Quando ho impostato il mio progetto per utilizzare VS Dev Server, non IIS, ho notato errori di schermata gialla invece di pagine di errore personalizzate.
Jesse Webb

1
@ SerjSagan Ma sembra che tu stia ricevendo errori di schermata blu di IIS, al contrario dei classici errori di schermata gialla. Questo mi fa presumere che tu stia utilizzando una qualche forma di IIS. Se è così, leggi l'articolo a cui ho collegato nella prima frase, in particolare l'ultima sezione intitolata: "Poche note importanti". Dice:To able to overwrite global settings in global IIS settings (file: C:\Windows\System32\inetsrv\config \applicationHost.config) should be: <section name="httpErrors" overrideModeDefault="Allow" />
Jesse Webb

3
@SerjSagan Cambiando errorMode in DetailedLocalOnly o Custom verrà visualizzata la tua pagina.
Daniel P

8

In caso di utilizzo di ASP.NET MVC5

public class ExceptionPublisherExceptionFilter : IExceptionFilter
    {
        private static Logger _logger = LogManager.GetCurrentClassLogger();
        public void OnException(ExceptionContext exceptionContext)
        {
            var exception = exceptionContext.Exception;
            var request = exceptionContext.HttpContext.Request;
            // HttpException httpException = exception as HttpException;
            // Log this exception with your logger
            _logger.Error(exception.Message);
        }
    }

Lo si può trovare in FilterConfig.csdi App_Startcartella.


1

Mi piace la risposta di Mark con ExceptionFilter, ma un'altra opzione, se tutti i controller derivano dallo stesso controller di base, è semplicemente sovrascrivere OnException nel controller di base. Puoi eseguire la registrazione e l'invio di e-mail lì. Questo ha il vantaggio di poter utilizzare qualsiasi dipendenza che hai già inserito nel tuo controller di base con il tuo contenitore IoC.

Puoi ancora usare l'IoC con un IExceptionFilter, ma è un po 'più complicato configurare le tue associazioni.


0

Per quanto ne so, stai passando il controllo alla pagina specificata nel parametro url e la notifica dell'evento si troverebbe qui, invece che Application_Error

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

Molte informazioni sono disponibili qui: http://support.microsoft.com/kb/306355


Grazie Chris, ho letto la documentazione e suggerisce che Application_Error verrà chiamato quando si verifica un errore non gestito (cosa che mi aspetto) e che se Server.ClearError () non viene chiamato la sezione web.config customErrors sarà il punto di gestione finale. Tuttavia, l'abilitazione di customErrors è ciò che impedisce l'esecuzione di Application_Error.
WDuffy

La documentazione a cui ti sei collegato parla dell'applicazione ASP.NET e sembra che le applicazioni web MVC3 si comportino diversamente.
Jesse Webb

0

Per aggirare questo problema ho finito per lasciare customerrors disabilitato e gestire tutti gli errori dall'evento Application_Error in global.asax. È leggermente complicato con MVC poiché non volevo restituire un reindirizzamento 301, volevo restituire codici di errore adeguati. Maggiori dettagli possono essere visualizzati sul mio blog all'indirizzo http://www.wduffy.co.uk/blog/using-application_error-in-asp-net-mvcs-global-asax-to-handle-errors/ ma il codice finale è elencate di seguito...

void Application_Error(object sender, EventArgs e)
{
    var error = Server.GetLastError();
    var code = (error is HttpException) ? (error as HttpException).GetHttpCode() : 500;

    if (code != 404)
    {
            // Generate email with error details and send to administrator
    }

    Response.Clear();
    Server.ClearError();

    string path = Request.Path;
    Context.RewritePath(string.Format("~/Errors/Http{0}", code), false);
    IHttpHandler httpHandler = new MvcHttpHandler();
    httpHandler.ProcessRequest(Context);
    Context.RewritePath(path, false);
}

Ed ecco il controller

public class ErrorsController : Controller
{

    [HttpGet]
    public ActionResult Http404(string source)
    {
            Response.StatusCode = 404;
            return View();
    }

    [HttpGet]
    public ActionResult Http500(string source)
    {
            Response.StatusCode = 500;
            return View();
    }

}

Ho provato la tua soluzione ma non ha funzionato per me. Con le linee Response.StatusCode = ###;, mostrava le pagine di errore MVC integrate in C:\inetpub\custerr\en-US. Inoltre non mi piaceva l'idea di richiamare manualmente HttpHandlers o Controller dal mio metodo Application_Error (). Sono contento che tu abbia trovato una soluzione al tuo problema, so che tipo di mal di testa mi ha dato.
Jesse Webb

0

Questo post di blog mi ha aiutato:

http://asp-net.vexedlogic.com/2011/04/23/asp-net-maximum-request-length-exceeded/

Se utilizzi IIS 7.0 o versioni successive, puoi modificare il file Web.config per gestire le richieste troppo grandi. Ci sono alcuni avvertimenti, ma ecco un esempio:

<system.webServer>
  <security>
    <requestFiltering>
      <requestLimits maxAllowedContentLength="1048576" />
    </requestFiltering>
  </security>
  <httpErrors errorMode="Custom" existingResponse="Replace">
    <remove statusCode="404" subStatusCode="13" />
    <error statusCode="404" subStatusCode="13" prefixLanguageFilePath="" path="/UploadTooLarge.htm" responseMode="Redirect" />
  </httpErrors>
</system.webServer>

Ci sono ulteriori dettagli su questi elementi del file di configurazione qui:

http://www.iis.net/ConfigReference/system.webServer/security/requestFiltering/requestLimits

Il codice di stato 404.13 è definito come "Lunghezza del contenuto troppo grande". Una cosa importante da notare è che maxAllowedContentLengthè specificato in byte. Questo è diverso maxRequestLengthdall'impostazione che trovi nella <system.web>sezione, che è specificata in kilobyte.

<system.web>
  <httpRuntime maxRequestLength="10240" />
</system.web>

Notare inoltre che l' pathattributo deve essere un percorso assoluto quando responseModeè Redirect, quindi anteporre il nome della directory virtuale, se pertinente. Le risposte informative di Jesse Webb mostrano come farlo responseMode="ExecuteURL", e penso che anche quell'approccio funzionerebbe bene.

Questo approccio non funziona se stai sviluppando utilizzando Visual Studio Development Server (Cassini, il server Web integrato in Visual Studio). Presumo che funzionerebbe in IIS Express, ma non l'ho testato.


0

Avevo lo stesso problema di Application_Error()non essere stato colpito. Ho provato di tutto, finché alla fine non sono riuscito a capire cosa stava succedendo. Avevo del codice personalizzato in un evento ELMAH che aggiungeva JSON all'email che invia, e c'era un errore nullo lì dentro!

La correzione dell'errore interno ha consentito al codice di continuare con l' Application_Error()evento come previsto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.