поймать все необработанные исключения в ASP.NET Web Api

113

Как мне перехватить все необработанные исключения, возникающие в ASP.NET Web Api, чтобы я мог их регистрировать?

Пока я пробовал:

  • Создать и зарегистрировать ExceptionHandlingAttribute
  • Реализуйте Application_Errorметод вGlobal.asax.cs
  • Подписываться на AppDomain.CurrentDomain.UnhandledException
  • Подписываться на TaskScheduler.UnobservedTaskException

Он ExceptionHandlingAttributeуспешно обрабатывает исключения, которые возникают в методах действий контроллера и фильтрах действий, но другие исключения не обрабатываются, например:

  • Исключения, возникающие, когда IQueryableметод, возвращаемый методом действия, не может выполнить
  • Исключения, создаваемые обработчиком сообщений (т.е. HttpConfiguration.MessageHandlers)
  • Исключения, возникающие при создании экземпляра контроллера

По сути, если исключение приведет к возврату клиенту 500 Internal Server Error, я хочу, чтобы это было зарегистрировано. Реализация Application_Errorхорошо справилась с этой задачей в веб-формах и MVC - что я могу использовать в веб-API?

Джо Дэйли
источник
Вы пробовали использовать мониторинг работоспособности ASP.NET ? Просто включите его и посмотрите, не регистрируются ли ваши исключения в журнале событий.
Джон Сондерс
Мониторинг работоспособности перехватывает исключения моего конвейера MVC, но не исключения моего конвейера Web Api.
Джо Дейли
Спасибо - Мне потребовалось время, чтобы понять, почему я не могу зарегистрировать свои проблемы с внедрением конструктора / зависимостей, когда я думал, что у меня уже отсортировано ведение журнала WebAPI ...
Облет

Ответы:

156

Теперь это возможно с WebAPI 2.1 (см. Что нового ):

Создайте одну или несколько реализаций IExceptionLogger. Например:

public class TraceExceptionLogger : ExceptionLogger
{
    public override void Log(ExceptionLoggerContext context)
    {
        Trace.TraceError(context.ExceptionContext.Exception.ToString());
    }
}

Затем зарегистрируйтесь с помощью HttpConfiguration вашего приложения внутри обратного вызова конфигурации, например:

config.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());

или напрямую:

GlobalConfiguration.Configuration.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());
декады
источник
7
@NeilBarnwell Да, Web API 2.1 соответствует версии 5.1.0 сборки System.Web.Http . Таким образом, вам нужна эта версия или выше, чтобы использовать решение, описанное здесь. См. Версии пакета
nuget
2
Некоторые 500 ошибок по-прежнему не обнаруживаются, например. HttpException - удаленный хост закрыл соединение. Есть ли еще место для global.asax Application_Error для обработки ошибок вне обработки веб-API?
Avner
11
Мне нравится, насколько подробны официальные документы на msdn, и 99% разработчиков действительно хотят только 8 строк кода для регистрации ошибок.
Rocklan
20

Ответ Yuval предназначен для настройки ответов на необработанные исключения, обнаруженные веб-API, а не для ведения журнала, как указано на связанной странице . См. Подробности в разделе «Когда использовать» на странице. Регистратор вызывается всегда, но обработчик вызывается только тогда, когда может быть отправлен ответ. Короче говоря, используйте средство ведения журнала для ведения журнала и обработчик для настройки ответа.

Кстати, я использую сборку v5.2.3, а в ExceptionHandlerклассе нет HandleCoreметода. Я думаю, что эквивалент Handle. Однако просто подклассы ExceptionHandler(как в ответе Ювала) не работают. В моем случае я должен реализовать IExceptionHandlerследующее.

internal class OopsExceptionHandler : IExceptionHandler
{
    private readonly IExceptionHandler _innerHandler;

    public OopsExceptionHandler (IExceptionHandler innerHandler)
    {
        if (innerHandler == null)
            throw new ArgumentNullException(nameof(innerHandler));

        _innerHandler = innerHandler;
    }

    public IExceptionHandler InnerHandler
    {
        get { return _innerHandler; }
    }

    public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
    {
        Handle(context);

        return Task.FromResult<object>(null);
    }

    public void Handle(ExceptionHandlerContext context)
    {
        // Create your own custom result here...
        // In dev, you might want to null out the result
        // to display the YSOD.
        // context.Result = null;
        context.Result = new InternalServerErrorResult(context.Request);
    }
}

Обратите внимание, что, в отличие от регистратора, вы регистрируете свой обработчик, заменяя обработчик по умолчанию, а не добавляя.

config.Services.Replace(typeof(IExceptionHandler),
    new OopsExceptionHandler(config.Services.GetExceptionHandler()));
Дуок Тран
источник
1
Это отличное решение, это должно быть общепринятым решением «поймать или зарегистрировать все ошибки». Я никогда бы не понял, почему это не сработало для меня, когда я просто расширял ExceptionHandler.
Rajiv
Отличное решение. Как только конвейер MVC был загружен для запроса, это отлично работает. IIS по-прежнему обрабатывает исключения до этого момента, в том числе при раскручивании OWIN в startup.cs. Однако в какой-то момент после завершения обработки файла startup.cs он отлично справляется со своей задачей.
Gustyn
18

Чтобы ответить на мой собственный вопрос, это невозможно!

Обработка всех исключений, вызывающих внутренние ошибки сервера, кажется базовой возможностью, которую должен иметь веб-API, поэтому я отправил в Microsoft запрос на глобальный обработчик ошибок для веб-API :

https://aspnetwebstack.codeplex.com/workitem/1001

Если вы согласны, перейдите по этой ссылке и проголосуйте за нее!

А пока в отличной статье « Обработка исключений веб-API ASP.NET» показано несколько различных способов отлова нескольких различных категорий ошибок. Это сложнее, чем должно быть, и он не улавливает все внутренние ошибки сервера, но это лучший подход, доступный сегодня.

Обновление: глобальная обработка ошибок теперь реализована и доступна в ночных сборках! Он будет выпущен в ASP.NET MVC v5.1. Вот как это будет работать: https://aspnetwebstack.codeplex.com/wikipage?title=Global%20Error%20Handling

Джо Дэйли
источник
кажется причиной использовать контроллер для вызовов ajax вместо веб-api .. границы уже размыты .. хотя, если ELMAH сможет захватить это, возможно, есть способ
Sonic Soul
4
В Web API 2.1 добавлена ​​глобальная обработка ошибок. Подробнее см. Мой ответ.
декабрь
10

Вы также можете создать глобальный обработчик исключений, реализовав IExceptionHandlerинтерфейс (или унаследовав ExceptionHandlerбазовый класс). Он будет последним в цепочке выполнения после всех зарегистрированных IExceptionLogger:

IExceptionHandler обрабатывает все необработанные исключения из всех контроллеров. Это последний в списке. Если возникает исключение, сначала вызывается IExceptionLogger, затем ExceptionFilters контроллера и, если все еще не обработано, реализация IExceptionHandler.

public class OopsExceptionHandler : ExceptionHandler
{
    public override void HandleCore(ExceptionHandlerContext context)
    {
        context.Result = new TextPlainErrorResult
        {
            Request = context.ExceptionContext.Request,
            Content = "Oops! Sorry! Something went wrong."        
        };
    }

    private class TextPlainErrorResult : IHttpActionResult
    {
        public HttpRequestMessage Request { get; set; }

        public string Content { get; set; }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            HttpResponseMessage response = 
                             new HttpResponseMessage(HttpStatusCode.InternalServerError);
            response.Content = new StringContent(Content);
            response.RequestMessage = Request;
            return Task.FromResult(response);
        }
    }
}

Подробнее об этом здесь .

Юваль Ицчаков
источник
Просто любопытно, где это прописано?
ThunD3eR
-1

У вас могут быть существующие блоки try-catch, о которых вы не знаете.

Я думал, что мой новый global.asax.Application_Errorметод не вызывается постоянно для необработанных исключений в нашем устаревшем коде.

Затем я нашел несколько блоков try-catch в середине стека вызовов, которые вызывали Response.Write в тексте исключения. Вот и все. Вывалил текст на экран, а затем убил исключение как камень.

Итак, исключения обрабатывались, но ничего полезного не делалось. Как только я удалил эти блоки try-catch, исключения, как и ожидалось, распространялись на метод Application_Error.

Ресурс
источник