Udało mi się obejść ten problem, używając następującej konfiguracji w formularzach internetowych asp.net przy użyciu platformy .NET 3.5.
Wzorzec, który zaimplementowałem, omija niestandardowe rozwiązanie przekierowania .NET w pliku web.config, ponieważ napisałem własne, aby obsługiwać wszystkie scenariusze z poprawnym kodem stanu HTTP w nagłówku.
Po pierwsze, sekcja customErrors w pliku web.config wygląda następująco:
<customErrors mode="RemoteOnly" defaultRedirect="~/error.htm" />
Ta konfiguracja zapewnia, że tryb CustomErrors jest włączony, ustawienie, którego będziemy potrzebować później, i zapewnia opcję all-else-fail dla defaultRedirect error.htm. Przyda się to, gdy nie mam modułu obsługi określonego błędu lub jest coś w rodzaju zerwanego połączenia z bazą danych.
Po drugie, oto globalne zdarzenie asax Error:
protected void Application_Error(object sender, EventArgs e)
{
HandleError();
}
private void HandleError()
{
var exception = Server.GetLastError();
if (exception == null) return;
var baseException = exception.GetBaseException();
bool errorHandled = _applicationErrorHandler.HandleError(baseException);
if (!errorHandled) return;
var lastError = Server.GetLastError();
if (null != lastError && HttpContext.Current.IsCustomErrorEnabled)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(lastError.GetBaseException());
Server.ClearError();
}
}
Ten kod przekazuje odpowiedzialność za obsługę błędu na inną klasę. Jeśli błąd nie jest obsługiwany, a CustomErrors jest włączony, oznacza to, że mamy przypadek, w którym jesteśmy na produkcji i jakoś błąd nie został obsłużony. Wyczyścimy to tutaj, aby użytkownik nie mógł go zobaczyć, ale zaloguj się do Elmah, abyśmy wiedzieli, co się dzieje.
Klasa applicationErrorHandler wygląda następująco:
public bool HandleError(Exception exception)
{
if (exception == null) return false;
var baseException = exception.GetBaseException();
Elmah.ErrorSignal.FromCurrentContext().Raise(baseException);
if (!HttpContext.Current.IsCustomErrorEnabled) return false;
try
{
var behavior = _responseBehaviorFactory.GetBehavior(exception);
if (behavior != null)
{
behavior.ExecuteRedirect();
return true;
}
}
catch (Exception ex)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
}
return false;
}
Ta klasa zasadniczo używa wzorca polecenia, aby zlokalizować odpowiednią procedurę obsługi błędów dla typu zgłoszonego błędu. Ważne jest, aby używać Exception.GetBaseException () na tym poziomie, ponieważ prawie każdy błąd zostanie opakowany w wyjątek wyższego poziomu. Na przykład wykonanie „rzutu nowego System.Exception ()” z dowolnej strony aspx spowoduje odebranie wyjątku HttpUnhandledException na tym poziomie, a nie System.Exception.
Kod „fabryczny” jest prosty i wygląda następująco:
public ResponseBehaviorFactory()
{
_behaviors = new Dictionary<Type, Func<IResponseBehavior>>
{
{typeof(StoreException), () => new Found302StoreResponseBehavior()},
{typeof(HttpUnhandledException), () => new HttpExceptionResponseBehavior()},
{typeof(HttpException), () => new HttpExceptionResponseBehavior()},
{typeof(Exception), () => new Found302DefaultResponseBehavior()}
};
}
public IResponseBehavior GetBehavior(Exception exception)
{
if (exception == null) throw new ArgumentNullException("exception");
Func<IResponseBehavior> behavior;
bool tryGetValue = _behaviors.TryGetValue(exception.GetType(), out behavior);
if (!tryGetValue)
_behaviors.TryGetValue(typeof(Exception), out behavior);
if (behavior == null)
Elmah.ErrorSignal.FromCurrentContext().Raise(
new Exception(
"Danger! No Behavior defined for this Exception, therefore the user might have received a yellow screen of death!",
exception));
return behavior();
}
W końcu mam rozszerzalny schemat obsługi błędów. W każdym ze zdefiniowanych „zachowań” mam niestandardową implementację dla typu błędu. Na przykład wyjątek HTTP zostanie sprawdzony pod kątem kodu stanu i odpowiednio obsłużony. Kod stanu 404 będzie wymagał Server.Transfer zamiast Request.Redirect, wraz z odpowiednim kodem stanu zapisanym w nagłówku.
Mam nadzieję że to pomoże.