如果抛出异常,.Net Web API 有时根本不返回任何响应

jps*_*ack 5 .net c# asp.net-web-api

首先介绍一下背景。
我在 Visual Studio 2017 社区中使用 .Net Framework 4.6.1、Microsoft.AspNet.WebApi 5.2.4。

我的 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)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我采取了一个解决方法,如下所示:

[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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我将不胜感激任何如何调试此问题的想法或任何修复它的建议。

jid*_*idh 1

您可以尝试继承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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

并称其为

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);
}
Run Code Online (Sandbox Code Playgroud)

GlobalExceptionHandler : ExceptionHandler