Мне удалось обойти эту проблему с помощью следующей настройки в веб-формах asp.net с использованием .NET 3.5.
Шаблон, который я реализовал, обходит пользовательское решение перенаправления .NET в web.config, поскольку я написал свой собственный для обработки всех сценариев с правильным кодом состояния HTTP в заголовке.
Во-первых, раздел customErrors в web.config выглядит следующим образом:
<customErrors mode="RemoteOnly" defaultRedirect="~/error.htm" />
Эта настройка гарантирует, что режим CustomErrors включен, этот параметр нам понадобится позже, и предоставляет опцию all-else-fails для defaultRedirect файла error.htm. Это пригодится, когда у меня нет обработчика для конкретной ошибки или что-то вроде разрыва соединения с базой данных.
Во-вторых, вот глобальное событие 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();
}
}
Этот код передает ответственность за обработку ошибки другому классу. Если ошибка не обрабатывается и CustomErrors включен, это означает, что у нас есть случай, когда мы находимся в производстве, и каким-то образом ошибка не была обработана. Мы очистим его здесь, чтобы пользователь не увидел его, но войдите в систему в Elmah, чтобы мы знали, что происходит.
Класс applicationErrorHandler выглядит следующим образом:
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;
}
Этот класс по существу использует шаблон команды, чтобы найти соответствующий обработчик ошибок для типа выдаваемой ошибки. На этом уровне важно использовать Exception.GetBaseException (), потому что почти каждая ошибка будет заключена в исключение более высокого уровня. Например, выполнение «throw new System.Exception ()» из любой страницы aspx приведет к получению HttpUnhandledException на этом уровне, а не System.Exception.
«Заводской» код прост и выглядит так:
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);
//default value here:
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();
}
В конце концов, у меня есть расширяемая схема обработки ошибок. В каждом определенном «поведении» у меня есть пользовательская реализация для типа ошибки. Например, исключение Http будет проверено на наличие кода состояния и обработано соответствующим образом. Код состояния 404 потребует Server.Transfer вместо Request.Redirect вместе с соответствующим кодом состояния, записанным в заголовке.
Надеюсь, это поможет.