c# – 如果抛出Exception,.Net Web API偶尔会返回没有响应

首先是一些背景知识.

我在Visual Studio 2017社区中使用.Net Framework 4.6.1,Microsoft.AspNet.WebApi 5.2.4.

我的ApiController的实现端点会抛出预期的异常,例如,如果不满足某些要求.我添加了全局ExceptionFilterAttribute和ExceptionHandler来处理这些异常并返回正确的响应.
异常是一种继承System.Exception的类型.
这只是偶尔按预期工作.每隔一秒或三分之一或有时第五(没有实际模式)请求api服务器根本不返回任何响应,例如例如Postman说:“无法得到任何回应”.
为了测试这个,我使用了具有相同输入的相同端点.

以下是我为了更好地了解问题所做的一些事情:
我向Global.asax添加了异常日志记录(以捕获First Chance Exceptions)
我订阅了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;
    }
}

我将不胜感激任何想法如何调试此或任何建议来解决它.

最佳答案 你可以尝试从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

点赞