Ловля исключений в ASP.NET Core MVC - PullRequest
0 голосов
/ 09 сентября 2018

Я видел несколько способов перехвата исключений в ASP.NET MVC Core, но мне нужно понять, почему то, что я сделал, не работает так, как я ожидал.

Я добавил следующее:

public abstract class GenericActionFilter : ActionFilterAttribute
{
    protected readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);


    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        if (await OnBeforeActionExecutionAsync(context))
        {
            var executed = await next();

            if (executed.Exception != null && !executed.ExceptionHandled)
            {
                await OnExceptionAsync(context, executed.Exception);
            }
            else
            {
                await OnAfterActionExecutionAsync(context);
            }
        }
    }

    public virtual Task<bool> OnBeforeActionExecutionAsync(ActionExecutingContext context)
    {
        return Task.FromResult(true);
    }

    public virtual Task OnAfterActionExecutionAsync(ActionExecutingContext context)
    {
        return Task.CompletedTask;
    }

    public virtual Task OnExceptionAsync(ActionExecutingContext context, Exception ex)
    {
        return Task.CompletedTask;
    }
}

и используйте его так:

public class ExceptionFilter : GenericActionFilter
{
    public IntegrationScenarioSettings Settings { get; set; }


    public override Task OnExceptionAsync(ActionExecutingContext context, Exception ex)
    {
        context.Result = new ContentResult
        {
            Content = Settings.ApiExecutionExceptionMessage,
            StatusCode = (int)HttpStatusCode.ServiceUnavailable
        };

        //outputs to endpoint.log
        Logger.Error(ex);

        return Task.CompletedTask;
    }
}

Основной код в действии выдает исключение, а вместо того, чтобы видеть 503, а я все еще 500.

Что мне здесь не хватает?

1 Ответ

0 голосов
/ 09 сентября 2018

Чтобы сообщить конвейеру ASP.NET Core MVC о том, что вы обработали исключение, вам нужно установить ActionExecutedContext.ExceptionHandled в true. Поскольку в показанном вами коде этого нет, конвейер ASP.NET Core MVC использует собственную логику обработки ошибок, чтобы преобразовать необработанное исключение (которое он считает) в ответ 500.

Теперь - это свойство существует в ActionExecutedContext, а не в ActionExecutingContext (которое вы используете в своем коде). Это имеет смысл, поскольку ActionExecutingContext представляет состояние до выполнения действия, а ActionExecutedContext представляет состояние после выполнения действия. Это означает, что вам понадобится следующий набор изменений:

  1. Обновите вашу функцию OnExceptionAsync, чтобы она заняла ActionExecutedContext вместо ActionExecutingContext.
  2. Обновите вызов до OnExceptionAsync, указав executed вместо context. Пока вы здесь, вы также можете свернуть параметры метода до просто executed (я покажу это в коде ниже).
  3. Установите context.ExceptionHandled на true, как только вы обработаете исключение. :)

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

public abstract class GenericActionFilter : ActionFilterAttribute
{
    public override async Task OnActionExecutionAsync(ActionExecutingContext context,
        ActionExecutionDelegate next)
    {
        if (await OnBeforeActionExecutionAsync(context))
        {
            var executed = await next();

            if (executed.Exception != null && !executed.ExceptionHandled)
            {
                await OnExceptionAsync(executed); // #2.
            }
            else
            {
                // NOTE: You might want to use executed here too.
                await OnAfterActionExecutionAsync(context);
            }
        }
    }

    // ...

    public virtual Task OnExceptionAsync(ActionExecutedContext context) // #1.
    {
        return Task.CompletedTask;
    }
}

public class ExceptionFilter : GenericActionFilter
{    
    public override Task OnExceptionAsync(ActionExecutedContext context) // #1, #2.
    {
        Logger.Error(context.Exception); // #2 (single parameter).

        context.Result = new ContentResult { ... };
        context.ExceptionHandled = true; // #3.

        return Task.CompletedTask;
    }
}
...