Какой из них имеет приоритет, ExceptionFilter или ExceptionHandler в ASP.NET Web Api 2.0?

У меня есть глобальный ExceptionHandler в моем веб-api 2.0, который обрабатывает все необработанные исключения, чтобы вернуть приветственное сообщение об ошибке вызывающему абоненту. У меня также есть глобальный ExceptionFilter, который обрабатывает очень специфическое исключение в моем веб-api и возвращает конкретный ответ. ExceptionFilter динамически добавляется плагином к моему веб-апи, поэтому я не могу делать то, что он делает в моем ExceptionHandler.

Мне интересно, есть ли у меня как ExceptionHandler, так и ExceptionFilter, зарегистрированный глобально, какой из них будет приоритетным и будет выполнен первым? Сейчас я вижу, что ExceptionFilter выполняется до ExceptionHandler. И я также могу видеть, что в моем ExceptionFilter, если я создаю ответ, ExceptionHandler не выполняется.

Можно ли предположить, что:

  • ExceptionFilters выполняются до ExceptionHandlers.

  • Если ExceptionFilter создает ответ, ExceptionHandler не будет выполнен.

Ответ 1

Мне пришлось отлаживать System.Web.Http, чтобы найти ответ на мой вопрос. Итак, ответ:

  • Можно с уверенностью предположить, что ExceptionFilters будут выполнены до ExceptionHandlers

  • Если ExceptionFilter создает ответ, ExceptionHandler не будет выполнен.

Почему это так:

Когда у вас есть зарегистрированный ExceptionFilter для выполнения в глобальном масштабе или для действия вашего контроллера, базовый класс ApiController, из которого все наследуемые наследуемые контроллеры api будут обертывать результат в ExceptionFilterResult и вызывать его метод ExecuteAsync. Это код в ApiController, который делает это:

if (exceptionFilters.Length > 0)
{
    IExceptionLogger exceptionLogger = ExceptionServices.GetLogger(controllerServices);
    IExceptionHandler exceptionHandler = ExceptionServices.GetHandler(controllerServices);
    result = new ExceptionFilterResult(ActionContext, exceptionFilters, exceptionLogger, exceptionHandler,
        result);
}

return result.ExecuteAsync(cancellationToken);

Глядя на метод ExceptionFilterResult.ExecuteAsync:

try
{
    return await _innerResult.ExecuteAsync(cancellationToken);
}
catch (Exception e)
{
    exceptionInfo = ExceptionDispatchInfo.Capture(e);
}

// This code path only runs if the task is faulted with an exception
Exception exception = exceptionInfo.SourceException;
Debug.Assert(exception != null);

bool isCancellationException = exception is OperationCanceledException;

ExceptionContext exceptionContext = new ExceptionContext(
    exception,
    ExceptionCatchBlocks.IExceptionFilter,
    _context);

if (!isCancellationException)
{
    // We don't log cancellation exceptions because it doesn't represent an error.
    await _exceptionLogger.LogAsync(exceptionContext, cancellationToken);
}

HttpActionExecutedContext executedContext = new HttpActionExecutedContext(_context, exception);

// Note: exception filters need to be scheduled in the reverse order so that
// the more specific filter (e.g. Action) executes before the less specific ones (e.g. Global)
for (int i = _filters.Length - 1; i >= 0; i--)
{
    IExceptionFilter exceptionFilter = _filters[i];
    await exceptionFilter.ExecuteExceptionFilterAsync(executedContext, cancellationToken);
}

if (executedContext.Response == null && !isCancellationException)
{
    // We don't log cancellation exceptions because it doesn't represent an error.
    executedContext.Response = await _exceptionHandler.HandleAsync(exceptionContext, cancellationToken);
}

Вы можете видеть, что ExceptionLogger выполняется первым, тогда все ExceptionFilters выполняются, а если if executeContext.Response == null, ExceptionHandler выполняется.

Надеюсь, это полезно!