15

Mam globalny ExceptionHandler w moim api 2.0, który obsługuje wszystkie nieobsłużone wyjątki, aby zwrócić przyjazny komunikat o błędzie do osoby dzwoniącej api. Mam również globalny ExceptionFilter, który obsługuje bardzo specyficzny wyjątek w moim interfejsie API i zwraca określoną odpowiedź. ExceptionFilter jest dodawany dynamicznie przez wtyczkę do mojego interfejsu API, więc nie mogę zrobić tego, co robi w moim ExceptionHandler.Który z nich ma priorytet, ExceptionFilter lub ExceptionHandler w ASP.NET Web Api 2.0?

Zastanawiam się, czy mam zarejestrowany globalnie zarówno ExceptionHandler, jak i ExceptionFilter, który z nich będzie miał pierwszeństwo i zostanie wykonany jako pierwszy? Teraz widzę, że ExceptionFilter jest wykonywany przed ExceptionHandler. I widzę również, że w moim ExceptionFilter, jeśli utworzyć odpowiedź ExceptionHandler nie jest wykonywana.

Czy będzie bezpiecznie przyjąć, że:

  1. ExceptionFilters są wykonywane przed ExceptionHandlers.

  2. Jeśli ExceptionFilter tworzy odpowiedź, ExceptionHandler nie zostanie wykonany.

Odpowiedz

25

Musiałem debugować przez System.Web.Http, aby znaleźć odpowiedź na moje pytanie. Więc odpowiedź brzmi:

  1. Jest bezpiecznie założyć, że ExceptionFilters będą wykonywane przed ExceptionHandlers

  2. Jeśli ExceptionFilter tworzy odpowiedź ExceptionHandler nie zostanie wykonana.

Dlaczego to jest tak:

gdy masz ExceptionFilter zarejestrowany wykonać globalnie lub dla działania kontrolera, klasa bazowa ApiController z których wszystkie kontrolery api dziedziczyć będą zawijać wynik w ExceptionFilterResult i wywołaj jego metodę ExecuteAsync. Jest to kod w ApiController, który to robi:

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); 

Patrząc na metodzie 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); 
} 

widać, że ExceptionLogger jest wykonywany, potem wszystkie ExceptionFilters są Wykonane, a następnie jeśli wykonanoContext.Response == null, ExceptionHandler jest wykonywany.

Mam nadzieję, że przyda się to!