3

I have a traditional WebApi project on .NET 4.6.1 with a global ExceptionFilter which handles known exceptions to return a friendly message with a corresponding status code.

WebApiConfig.cs

public static void Register(HttpConfiguration config)
{
    config.MapHttpAttributeRoutes();

    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );

    config.Filters.Add(new MyExceptionFilterAttribute());
}

MyExceptionFilterAttribute.cs

internal sealed class GatewayExceptionFilterAttribute : ExceptionFilterAttribute
{
    private static readonly Dictionary<Type, HttpStatusCode> Exceptions = new Dictionary<Type, HttpStatusCode>
    {
        // HTTP 400
        [typeof(ArgumentException)] = HttpStatusCode.BadRequest,
        [typeof(ArgumentNullException)] = HttpStatusCode.BadRequest,
    };

    public override void OnException(HttpActionExecutedContext context)
    {
        var type = context.Exception.GetType();

        if (context.Exception is HttpResponseException)
            return;

        // Return 500 for unspecified errors
        var status = HttpStatusCode.InternalServerError;
        var message = Strings.ERROR_INTERNAL_ERROR;

        if (Exceptions.ContainsKey(type))
        {
            status = Exceptions[type];
            message = context.Exception.Message;
        }

        context.Response = context.Request.CreateErrorResponse(status, message);
    }
}

MyController.cs

public class MyController : ApiController
{
    public async Task<IHttpActionResult> Get()
    {
        await Task.Run(() => { throw new ArgumentNullException("Error"); });
        return Ok();
    }
}

When calling this as is, I will receive a 400.

However, when adding a reference to a .NETStandard 2.0 lib, the exception does not go to the ExceptionFilter and I get the following error:

{
    "message": "An error has occurred.",
    "exceptionMessage": "Method not found: 'System.Net.Http.HttpRequestMessage System.Web.Http.Filters.HttpActionExecutedContext.get_Request()'.",
    "exceptionType": "System.MissingMethodException",
    "stackTrace": "   at MyApi.Filters.MyExceptionFilterAttribute.OnException(HttpActionExecutedContext context)\r\n   at System.Web.Http.Filters.ExceptionFilterAttribute.OnExceptionAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Filters.ExceptionFilterAttribute.<ExecuteExceptionFilterAsyncCore>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Controllers.ExceptionFilterResult.<ExecuteAsync>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"
}

Any help is appreciated.

Kevin Aung
  • 803
  • 6
  • 12
  • Very similar problem described here with a conclusion https://stackoverflow.com/questions/38630076/asp-net-core-web-api-exception-handling – Vidmantas Blazevicius Feb 02 '18 at 19:47
  • I think these are 2 different problems. His problem was that he wanted the `ExceptionFilter` to do all the exception handling, even from other filters, which isn't how the `ExceptionFilter` was designed. Also, he ended up implementing a solution with ASP Core. I'm still on WebApi. I can't convert yet. – Kevin Aung Feb 02 '18 at 20:30

3 Answers3

3

I met same problem while working with .Net 4.6.2 Web API. I have defined the following exception handler in the Web project:

public class ExceptionHandlerAttribute : ExceptionFilterAttribute
{
    private readonly MyLogger _logger;

    private const string ExceptionOccuredLoggingTag = "ExceptionOccured";

    public ExceptionHandlerAttribute(MyLogger logger)
    {
        _logger = logger;
    }


    public override void OnException(HttpActionExecutedContext context)
    {
        ...
        _logger.Error(errorMessage,
            context.Exception,
            mostRecentFrame.Class.Name,
            mostRecentFrame.Method.Name,
            ExceptionOccuredLoggingTag);
        ...
    }
}

Where the MyLogger class was defined in located in another .NETStandart 2.0 dll. And it showed me same exception as above when trying to call it.

A workaround that worked for me here was to define a .Net framework adapter MyLoggerAdapter in another library (non .NETStandart 2.0) that the web project is referencing to (in my case a business layer library):

public interface ILoggerCompatable
{
    void Error(string message, object data, params string[] tags);
}

public class MyLoggerAdapter : ILoggerCompatable
{
    private readonly MyLogger _logger;

    public MyLoggerAdapter(MyLogger logger)
    {
        _logger = logger;
    }

    public void Error(string message, object data, params string[] tags) => _logger.Error(message, data, tags.Select(tag => (Tag)tag).ToArray());
}

So that nothing is visible from that source .NETStandart library and then inject it into the handler (via Autofac):

public class ExceptionHandlerAttribute : ExceptionFilterAttribute
{
    private readonly ILoggerCompatable _logger;

    private const string ExceptionOccuredLoggingTag = "ExceptionOccured";

    public ExceptionHandlerAttribute(ILoggerCompatable logger)
    {
        _logger = logger;
    }


    public override void OnException(HttpActionExecutedContext context)
    {
        ...
        _logger.Error(errorMessage,
            context.Exception,
            mostRecentFrame.Class.Name,
            mostRecentFrame.Method.Name,
            ExceptionOccuredLoggingTag);
        ...);
    }
}

And the filter registration looks like this:

var loggerCompatable = GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(ILoggerCompatable)) as ILoggerCompatable;
GlobalConfiguration.Configuration.Filters.Add(new ExceptionHandlerAttribute(loggerCompatable));

Hope this helps!

splash
  • 31
  • 2
1

i got the issue too.

after install .net framewokr 4.7.2 runtime, issue solved.

you can get .net framewokr 4.7.2 runtime here: https://www.microsoft.com/net/download

PS: this issue happened on windows 7 & windows server 2008 R2 which do not has .net framewokr 4.7.x installed, and not appear on windows 10 which has .net framewokr 4.7.x by default.

weiyu xiao
  • 11
  • 2
0

Sorry I haven't posted an answer. So we found out that some API's are not compatible when adding Standard projects to .NET Framework projects. Another example is access to User in a controller. Once you have a Standard project added to your .NET Framework ASP.NET project, you will not have access to this (might even throw an exception) at runtime.

For 100% compatibility, we ended up writing our new libraries in Standard while targeting .NET 4.6, 4.6.1, and 4.6.2 since our projects still run under these. We upload these packages to our nuget feed which allows our other applications to reference the correct binaries.

Hope this helps!

Kevin Aung
  • 803
  • 6
  • 12