Подтвердить что ты не робот

Отключить * все * обработку исключений в ASP.NET Web API 2 (чтобы освободить место для моего собственного)?

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

public override async Task Invoke(IOwinContext context)
{
    try
    {
        await Next.Invoke(context);
    }
    catch (Exception ex)
    {
        // Log error and return 500 response
    }
}

Однако некоторые из исключений, которые я хотел бы поймать, будут пойманы и конвертированы в HttpErrorResponse конвейером веб-API, прежде чем я смогу добраться до них. В этом процессе я теряю много деталей об ошибках, поэтому я не могу получить полезные трассировки стека при отладке и т.д. (Отладчик даже не останавливается, когда генерируется исключение - мне нужно вручную выполнить код и посмотреть где он терпит неудачу...).

Я попробовал добавить настраиваемый обработчик исключений со следующей реализацией:

public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
    var owinContext = context.Request.GetOwinContext();
    owinContext.Set(Constants.ContextKeys.Exception, context.Exception);
    return Task.FromResult(0);
}

зарегистрировался через config.Services.Replace(typeof(IExceptionHandler), new MyExceptionHandler()); в моей конфигурации запуска, но посмотрел на него после выполнения Next.Invoke(context) через

context.Get<Exception>(Constants.ContextKeys.Exception);

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

Есть ли способ полностью отключить встроенную обработку ошибок, чтобы мое собственное промежуточное ПО могло позаботиться об этом?

Уточнение, поскольку многие люди, похоже, неправильно понимают, что мне нужно:

  • Встроенная обработка ошибок в веб-интерфейсе позволяет получить некоторые (но не все) исключения и переписать их на 500 ответов.
  • Я хочу поймать все исключения, сделать некоторые записи, а затем исправить 500 ответов с информацией, которую я выбираю (для большинства из них см. следующий маркер).
  • Есть также некоторые исключения, которые приводят к ошибкам бизнес-логики, для которых я хочу вернуть 40x ошибок.
  • Я хочу, чтобы это было в верхней части (app) конвейера, то есть обертывания всего остального в жизненном цикле запроса
  • Я хочу обработать это с помощью OWIN, чтобы сделать его переносимым для будущего будущего самостоятельного сценария (т.е. не написано на камне, что это приложение всегда будет размещаться на модулях IIS - HTTP, Global.asax.cs и др. здесь не актуальны).
4b9b3361

Ответ 1

Обновить: Я писал об этом. При исследовании сообщения в блоге я нашел некоторый потенциал для улучшения; Я обновил соответствующие части этого ответа. Более подробно о том, почему я думаю, что это лучше, чем все другие предложения здесь, или поведение по умолчанию, прочитайте весь пост:)


Теперь я перешел к следующему подходу, который, похоже, работает нормально, даже если он не соответствует 100% тому, что я искал:

  • Создайте класс PassthroughExceptionHandler:

    public class PassthroughExceptionHandler : IExceptionHandler
    {
        public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
        {
            // don't just throw the exception; that will ruin the stack trace
            var info = ExceptionDispatchInfo.Capture(context.Exception);
            info.Throw();
            return Task.CompletedTask;
        }
    }
    
  • Пусть этот класс заменит службу IExceptionHandler веб-API:

    config.Services.Replace(typeof(IExceptionHandler), new PassthroughExceptionHandler());
    
  • Создайте класс промежуточного ПО, который делает то, что я хочу:

    public class ExceptionHandlerMiddleware
    {
        public override async Task Invoke(IOwinContext context)
        {
            try
            {
                await Next?.Invoke(context);
            }
            catch (Exception ex)
            {
                // handle and/or log
            }
        }
    }
    
  • Зарегистрируйте это промежуточное программное обеспечение сначала в стеке:

    app.Use<ExceptionHandlerMiddleware>()
       .UseStageMarker(PipelineStage.Authenticate)
       // other middlewares omitted for brevity
       .UseStageMarker(PipelineStage.PreHandlerExecute)
       .UseWebApi(config);
    

Я все равно награжу награду всем, кто придумал (истекает срок действия бонуса...) Я все еще ищу лучшее решение, которое, например, ломается, когда необработанное исключение выброшены. (Этот подход заставляет VS прерываться, когда я восстанавливаю исключение в обработчике, но исходный стек вызовов теряется, я должен установить точку останова на линии сбоя и снова отлаживать, чтобы перехватить состояние при вызове исключения.)

Ответ 2

Не уверен, что это сработает для вас, но у меня есть аналогичное требование отправить все ошибки обратно как JSON даже для не найденных ошибок. Я создал базовый контроллер и перегрузил ExecuteAsync, позволяя мне создавать собственные ответы.

public class ControllerBase : ApiController
{
    protected string ClassName = "ControllerBase::";

    public override System.Threading.Tasks.Task<HttpResponseMessage> ExecuteAsync(System.Web.Http.Controllers.HttpControllerContext controllerContext, System.Threading.CancellationToken cancellationToken)
    {
        try
        {
            System.Threading.Tasks.Task<HttpResponseMessage> TaskList = base.ExecuteAsync(controllerContext, cancellationToken);

            if (TaskList.Exception != null && TaskList.Exception.GetBaseException() != null)
            {
                JSONErrorResponse AsyncError = new JSONErrorResponse();
                AsyncError.ExceptionMessage = TaskList.Exception.GetBaseException().Message;
                AsyncError.ErrorMessage = string.Format("Unknown error {0} ExecuteAsync {1}", ClassName ,controllerContext.Request.RequestUri.AbsolutePath);
                AsyncError.HttpErrorCode = HttpStatusCode.BadRequest;

                HttpResponseMessage ErrorResponse = controllerContext.Request.CreateResponse(AsyncError.HttpErrorCode, AsyncError);

                return System.Threading.Tasks.Task.Run<HttpResponseMessage>(() => ErrorResponse);
            }
            return TaskList;
        }
        catch (Exception Error)
        {
            JSONErrorResponse BadParameters = new JSONErrorResponse();
            BadParameters.ExceptionMessage = Error.Message;
            BadParameters.ErrorMessage = string.Format("Method [{0}], or URL [{1}] not found, verify your request", controllerContext.Request.Method.Method, controllerContext.Request.RequestUri.AbsolutePath);
            BadParameters.HttpErrorCode = HttpStatusCode.NotFound;
            HttpResponseMessage ErrorResponse = controllerContext.Request.CreateResponse(BadParameters.HttpErrorCode, BadParameters);

            return System.Threading.Tasks.Task.Run<HttpResponseMessage>(() => ErrorResponse);
        }
    }
}

public class JSONErrorResponse
{
    //Possible message from exception
    public string ExceptionMessage { get; set; }
    //Possible custom error message
    public string ErrorMessage { get; set; }
    //Http error code
    public HttpStatusCode HttpErrorCode { get; set; }
}

Ответ 3

Вы также можете попытаться создать свой собственный активатор контроллера, иметь свой собственный обработчик исключений и попытаться использовать ExceptionFilterAttribute.

  • Создайте активатор вашего контроллера

    public class ExceptionHandlingControllerActivator : IHttpControllerActivator
    {
        private readonly IHttpControllerActivator _concreteActivator;
    
        public ExceptionHandlingControllerActivator(IHttpControllerActivator concreteActivator)
        {
            _concreteActivator = concreteActivator;
        }
    
        public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
        {
            try
            {
                return _concreteActivator.Create(request, controllerDescriptor, controllerType);
            }
            catch (Exception ex)
            {
                // do stuff with the exception
                throw new HttpResponseException(request.CreateResponse(HttpStatusCode.InternalServerError, new ResponseModel(ex)));
            }
        }
    }
    
  • Создать ExceptionFilterAttribute

    public class ExceptionHandlingFilter : ExceptionFilterAttribute
    {
        public override void OnException(HttpActionExecutedContext context)
        {
            // do stuff with the exception
    
            var request = context.Request;
            ResponseModel respMod = null;
    
            // Example: if debug constant is not defined, mask exception, otherwise create normal object with message, inner exception and stacktrace
    
            #if !DEBUG
            respMod = new ResponseModel(context.Exception, context.Exception.Message, true);
            #else
            respMod = new ResponseModel(context.Exception);
            #endif
    
            context.Response = request.CreateResponse(HttpStatusCode.InternalServerError, respMod);
        }
    }
    
  • ResponseModel - это класс, который я сериализую в JSON с помощью Formatters и возвращаю все ответы контроллера, поэтому клиент может идентифицировать данные об ошибках, а также успешный ответ в дополнение к коду состояния HTTP.

    config.Formatters.Clear(); // do not need any other
    config.Formatters.Add(new JsonMediaTypeFormatter());
    
  • Подключите

    // ... [cut] ...            
    config.Filters.Add(new ExceptionHandlingFilter());
    // ... [cut] ...
    config.Services.Replace(typeof(IHttpControllerActivator),
        new ExceptionHandlingControllerActivator(config.Services.GetHttpControllerActivator())
    );
    // ... [cut] ...
    app.UseWebApi(config);
    

Ответ 4

OWIN не должен обрабатывать такие исключения, потому что web api имеет встроенную встроенную обработку ошибок. OWIN предназначен для отдельного приложения. Если вы установите точку останова в методе HandleAsync для вашего обработчика исключений, вы сможете проверить контекстную переменную и просмотреть сведения об исключении.

Если вы пытаетесь сделать это только для целей отладки, установка точки останова должна позволить вам увидеть исключение. Если вам нужно регистрировать исключения, обработчик исключений - лучшее место для этого, на мой взгляд.

Надеюсь, что это поможет.

Ответ 5

Здесь что-то может помочь:

fooobar.com/info/61018/...

http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-21#global-error

По существу, есть некоторая встроенная поддержка для поиска, обработки и изменения ошибок.

Это выглядит примерно так:

public class ExceptionLogger : System.Web.Http.ExceptionHandling.ExceptionLogger
{
    Logger _logger;

    public ExceptionLogger(Logger logger)
    {
        _logger = logger;
    }

    public override void Log(ExceptionLoggerContext context)
    {
        _logger.Error(context.ExceptionContext.Exception.ToString());
    }
}