.Net Web API иногда не отвечает вообще, если выдается исключение - PullRequest
4 голосов
/ 06 марта 2019

Прежде всего немного фона.
Я использую .Net Framework 4.6.1, Microsoft.AspNet.WebApi 5.2.4 в сообществе Visual Studio 2017.

Конечные точки реализации моего ApiController, которые генерируют ожидаемые исключения, например, если определенные требования не выполняются. Я добавил глобальные ExceptionFilterAttribute и ExceptionHandler для обработки этих исключений и для возврата правильного ответа. Исключения относятся к типу, который унаследован от System.Exception.
Это работает только изредка, как задумано. Каждый второй, третий или иногда пятый (без реального шаблона) запрос API-сервер не возвращает вообще никакого ответа, например, например, Почтальон говорит: «Не удалось получить ответ».
Чтобы проверить это, я использовал ту же конечную точку с тем же входом.

Вот несколько вещей, которые я сделал, чтобы лучше понять проблему:
Я добавил регистрацию исключений в Global.asax (чтобы отловить исключения первого шанса)
Я подписался на событие Global.asax Application_Error
Я посмотрел журналы IIS

Никто из них не подошел ближе к этому вопросу. Исключение было обнаружено и зарегистрировано в Global.asax, как и ожидалось, но без дополнительной ошибки или исключения, которое могло бы дать мне больше информации о моей проблеме.

Вот мой код:
Я упростил функцию ApiController и удалил бизнес-логику.

[Route("test")]
[HttpGet]
public IHttpActionResult GetTest()
{
    throw new ObjectAlreadyExistsException("test");
    return ResponseFactory.CreateOkResponse(null);
}

public class ExceptionFilter : ExceptionFilterAttribute
{
    public override void OnException(HttpActionExecutedContext context)
    {
        if (context.Exception is ObjectAlreadyExistsException)
        {
            context.Response = ResponseFactory.CreateErrorResponseMessage(context.Exception.Message, new Error("OBJECT_ALREADY_EXISTS_ERROR", context.Exception.Message));
        }
        else if (context.Exception is ObjectNotFoundException)
        {
            context.Response = ResponseFactory.CreateErrorResponseMessage(context.Exception.Message, new Error("OBJECT_NOT_FOUND_ERROR", context.Exception.Message));
        }

        base.OnException(context);
    }
}

public class GlobalExceptionHandler : ExceptionHandler
{
    private static readonly ILogger Log = new LoggerConfiguration()
            .WriteTo.File(new CompactJsonFormatter(), Path.Combine(@Properties.Settings.Default.LOG_DIRECTORY, @"error.json"), rollOnFileSizeLimit: true, retainedFileCountLimit: 5, shared: true)
            .Enrich.WithWebApiControllerName()
            .Enrich.WithWebApiActionName()
            .Enrich.WithWebApiRouteTemplate()
            .Enrich.WithWebApiRouteData()
            .Enrich.With(new AuthTokenEnricher())
            .CreateLogger();

    public override void Handle(ExceptionHandlerContext context)
    {
        if (context != null && context.Exception != null)
        {
            Log.Error("Unexpected Internal Server Error {Exception}", context.Exception);
        }

        context.Result = ResponseFactory.CreateErrorResponse(HttpStatusCode.InternalServerError, "Unexpected Internal Server Error", new Error("INTERNAL_SERVER_ERROR", "This request failed because of an unexpected server error."));
    }
}

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {

        //Exception filters
        config.Filters.Add(new ExceptionFilter());
        config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());

        // Web API routes
        config.MapHttpAttributeRoutes();
    }
}

public class ObjectAlreadyExistsException : Exception
{
    public ObjectAlreadyExistsException(string message) : base(message)
    {
    }

    public ObjectAlreadyExistsException(string message, Exception inner) : base(message, inner)
    {
    }
}

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

[Route("test")]
[HttpGet]
public IHttpActionResult GetTest()
{
    try
    {
        throw new ObjectAlreadyExistsException("test");
    }
    catch (Exception ex)
    {
        return CustomExceptionHandler.Handle(ex);
    }
}

public class CustomExceptionHandler
{
    private static readonly ILogger Log = new LoggerConfiguration()
            .WriteTo.File(new CompactJsonFormatter(), Path.Combine(@Properties.Settings.Default.LOG_DIRECTORY, @"error.json"), rollOnFileSizeLimit: true, retainedFileCountLimit: 5, shared: true)
            .Enrich.WithWebApiControllerName()
            .Enrich.WithWebApiActionName()
            .Enrich.WithWebApiRouteTemplate()
            .Enrich.WithWebApiRouteData()
            .Enrich.With(new AuthTokenEnricher())
            .CreateLogger();

    public static IHttpActionResult Handle(Exception ex)
    {
        IHttpActionResult result = null;

        if (ex != null)
        {
            if (ex is ObjectAlreadyExistsException)
            {
                result = ResponseFactory.CreateErrorResponse(ex.Message, new Error("OBJECT_ALREADY_EXISTS_ERROR", ex.Message));
            }
            else if (ex is ObjectNotFoundException)
            {
                result = ResponseFactory.CreateErrorResponse(ex.Message, new Error("OBJECT_NOT_FOUND_ERROR", ex.Message));
            }
        }

        if (result == null)
        {
            if (ex != null)
            {
                Log.Error("Unexpected Internal Server Error {Exception}", ex);
            }

            result = ResponseFactory.CreateErrorResponse(HttpStatusCode.InternalServerError, "Unexpected Internal Server Error", new Error("INTERNAL_SERVER_ERROR", "This request failed because of an unexpected server error."));
        }

        return result;
    }
}

Буду признателен за любые идеи по отладке этого или за любые предложения по его исправлению.

1 Ответ

1 голос
/ 06 марта 2019

Можете ли вы попробовать наследовать от IHttpActionResult и использовать его как возврат исключения из вашего GlobalExceptionHandler

 private class ErrorMessageResult : IHttpActionResult
    {
        private readonly HttpResponseMessage _httpResponseMessage;
        private HttpRequestMessage _request;

        public ErrorMessageResult(HttpRequestMessage request, HttpResponseMessage httpResponseMessage)
        {
            _request = request;
            _httpResponseMessage = httpResponseMessage;
        }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            return Task.FromResult(_httpResponseMessage);
        }
    }

и назовите это как

public override void Handle(ExceptionHandlerContext context)
{
    var result = new HttpResponseMessage(HttpStatusCode.InternalServerError)
    {
        Content = new StringContent("Internal Server Error Occurred"),
        ReasonPhrase = "Exception"
    };

    context.Result = new ErrorMessageResult(context.Request, result);
}

С GlobalExceptionHandler : ExceptionHandler

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...