ASP.NET MVC Глобальная обработка ошибок - PullRequest
10 голосов
/ 27 февраля 2012

У меня есть собственный атрибут HandleError, который имеет дело с ошибками в конвейере MVC;У меня protected void Application_Error(object sender, EventArgs e) метод на моем Global.asax, который обрабатывает ошибки извне конвейера.

Я столкнулся со сценарием, который, как я знал, не был возможен;При реализации DI существует зависимость для connectionString, которая берется из файла конфигурации приложения.

Поскольку строка подключения еще не существует, возникает ошибка при создании контроллера, это обычно приводит кApplication_Error обработчик запускается, и отображается правильная страница ошибки (путем рендеринга частичного представления в виде строки и отправки его в качестве ответа, а в случае неудачи он просто записывает «фатальное исключение» в ответ.

За исключением этого случая, я получаю нечеткий желтый экран смерти ASP.NET по умолчанию «Ошибка времени выполнения», сообщающий мне:

Ошибка времени выполнения

Описание: Ошибка приложения произошла всервер. Текущие пользовательские настройки ошибок для этого приложения не позволяют просматривать подробности ошибки приложения.

Детали: чтобы детали этого конкретного сообщения об ошибке можно было просматривать на локальном сервере, создайтетег в файле конфигурации "web.config", расположенный в корневом каталоге cuрент веб-приложение.Для этого тега должен быть установлен атрибут «mode» «RemoteOnly».Чтобы детали были доступны для просмотра на удаленных компьютерах, установите для «mode» значение «Off».

У меня нет defaultRedirect, установленного в моем customErrors, и при этом он не включенOff, потому что я не хочу перенаправлять, а отображать ошибки на той же странице, на которой находится пользователь, избегая ненужного перенаправления.

Как я могу обработать такой сценарий?И в чем даже причина того, что он ведет себя так, а не как любая другая ошибка вне контроллера?

Я понимаю, что это не часто случается, но я хотел бы иметь возможность остановить YSOD (частично потому, что я хочучтобы скрыть технологию, которую я использую, но в основном потому, что она совсем не симпатична и не удобна для пользователя)

Я даже пытался зарегистрировать обработчик для UnhandledExceptions, но он также не срабатывал.

AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

Код, который в конечном итоге производит это:

return ConfigurationManager.ConnectionStrings[key].ConnectionString;, где ConnectionStrings[key] равно null.

Обновление

Вот как обрабатываются ошибки приложения:

    protected void Application_Error(object sender, EventArgs e)
    {
        this.HandleApplicationError(new ResourceController());
    }

    public static void HandleApplicationError(this HttpApplication application, BaseController controller)
    {
        if (application == null)
        {
            throw new ArgumentNullException("application");
        }
        if (controller == null)
        {
            throw new ArgumentNullException("controller");
        }
        application.Response.Clear();
        Exception exception = application.Server.GetLastError();
        LogApplicationException(application.Response, exception);
        try
        {
            RenderExceptionViewResponse(application, exception, controller);
        }
        catch (Exception exceptionRenderingView) // now we're in trouble. let's be as graceful as possible.
        {
            RenderExceptionTextResponse(application, exceptionRenderingView);
        }
        finally
        {
            application.Server.ClearError();
        }
    }

    private static void LogApplicationException(HttpResponse response, Exception exception)
    {
        if (exception is HttpException)
        {
            HttpException httpException = (HttpException)exception;
            if (httpException.GetHttpCode() == (int)HttpStatusCode.NotFound)
            {
                _log.Debug(Resources.Error.WebResourceNotFound, httpException);
                response.Status = Resources.Constants.NotFound;
                return;
            }
        }
        _log.Error(Resources.Error.UnhandledException, exception);
    }

    private static void RenderExceptionViewResponse(HttpApplication application, Exception exception, BaseController controller)
    {
        if (!RenderAsJsonResponse(application, Resources.User.UnhandledExceptionJson))
        {
            ErrorViewModel model = WebUtility.GetErrorViewModel(exception);
            string result = controller.RenderViewToString(Resources.Constants.ErrorViewName, model);
            application.Response.Write(result);
        }
    }

    private static void RenderExceptionTextResponse(HttpApplication application, Exception exceptionRenderingView)
    {
        application.Response.Clear();

        if (!RenderAsJsonResponse(application, Resources.User.FatalExceptionJson))
        {
            application.Response.Write(Resources.User.FatalException);
        }
        _log.Fatal(Resources.Error.FatalException, exceptionRenderingView);
    }

    private static bool RenderAsJsonResponse(HttpApplication application, string message)
    {
        if (application.Request.IsAjaxRequest())
        {
            application.Response.Status = Resources.Constants.HttpSuccess;
            application.Response.ContentType = Resources.Constants.JsonContentType;
            application.Response.Write(message);
            return true;
        }
        return false;
    }

Это атрибут, который я использую для украшения своего базового контроллера:

public class ErrorHandlingAttribute : HandleErrorAttribute
{
    public Type LoggerType { get; set; }

    public ErrorHandlingAttribute()
        : this(typeof(ErrorHandlingAttribute))
    {
    }

    public ErrorHandlingAttribute(Type loggerType)
    {
        LoggerType = loggerType;
    }

    public override void OnException(ExceptionContext filterContext)
    {
        if (filterContext.ExceptionHandled)
        {
            return;
        }
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            OnAjaxException(filterContext);
        }
        else
        {
            OnRegularException(filterContext);
        }
    }

    internal protected void OnRegularException(ExceptionContext filterContext)
    {
        Exception exception = filterContext.Exception;

        ILog logger = LogManager.GetLogger(LoggerType);
        logger.Error(Resources.Error.UnhandledException, exception);

        filterContext.HttpContext.Response.Clear();

        ErrorViewModel model = WebUtility.GetErrorViewModel(exception);
        filterContext.Result = new ViewResult
        {
            ViewName = Resources.Constants.ErrorViewName,
            ViewData = new ViewDataDictionary(model)
        };
        filterContext.ExceptionHandled = true;
    }

    internal protected void OnAjaxException(ExceptionContext filterContext)
    {
        Exception exception = filterContext.Exception;

        ILog logger = LogManager.GetLogger(LoggerType);
        logger.Error(Resources.Error.UnhandledAjaxException, exception);

        filterContext.HttpContext.Response.Clear();
        filterContext.HttpContext.Response.Status = Resources.Constants.HttpSuccess;

        string errorMessage = WebUtility.GetUserExceptionMessage(exception, true);

        filterContext.Result = new ExceptionJsonResult(new[] { errorMessage });
        filterContext.ExceptionHandled = true;
    }
}

И это мое customErrors:

<customErrors mode="On" />

Как вы можете видеть, они довольно обширны, однако они даже не запускаются в случае доступа к ConnectionStrings, где ConnectionString не существует;это немного озадачивает.

запускает в любом контроллере, содержащем исключение или исключения, не входящие в контроллер, поэтому я не понимаю, почему этот случай отличается.

Ответы [ 3 ]

7 голосов
/ 27 февраля 2012

Application_Error может быть рекомендуемым способом обработки ошибок в ASP.NET WebForms.Но не в MVC.

У нас есть фильтры ошибок, которые заботятся о нас.Проблема с фильтром заключается в том, что он работает только тогда, когда был вызван контроллер.В чем проблема с ошибками 404 и 401 (не найдены и авторизация) и с проблемой соединения с базой данных.Я не понимаю, почему перенаправление должно быть проблемой?

Я рассматриваю правильную обработку ошибок в статье блога: http://blog.gauffin.org/2011/11/how-to-handle-errors-in-asp-net-mvc/

0 голосов
/ 21 января 2014

Чтобы быть правильными для наших посетителей и поисковых систем, мы должны возвращать значимые коды состояния HTTP в случае возникновения ошибок, связанных с нашим сайтом.Неверно возвращать HTTP-код состояния 200 при ошибке, даже если в то же время мы возвращаем представление, объясняющее, что произошла ошибка.если пользователь вводит неправильный адрес (наиболее частая ошибка пользователя), мы должны вернуть код состояния HTTP 404, а не возвращать или перенаправлять в представление, где будет возвращен код состояния 200.Краткое и краткое изложение рекомендаций ЗДЕСЬ .

0 голосов
/ 27 февраля 2012

Вы не показали, как именно вы обрабатываете ошибки в событии Application_Error, и как реализован ваш пользовательский атрибут HandleAttribute, поэтому трудно догадаться, в чем может быть проблема. Распространенная проблема, за которой нужно следить при реализации Application_Error, заключается в том, что вы отображаете какое-то представление об ошибке, которое само выдает ошибку. Представьте, например, представление об ошибке, которое зависит от макета, и внутри этого макета вы вызываете хелпер Html.Action для визуализации содержимого другого действия, которое само выполняет доступ к базе данных и тому подобное. Ваши сообщения об ошибках должны быть как можно более статичными. В идеале у вас должен быть другой макет, чтобы избежать подобных ситуаций.

Я также могу предложить вам использовать следующий подход для обработки ошибок.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...