Search code examples
c#asp.net-web-apiasp.net-web-api2owin-middleware

AspNet WebApi hosted with Owin.Host.SystemWeb exception middleware doesn't catch POST requests


I've got a project that uses AspNet WebApi 2 hosted in Owin using Microsoft.Owin.Host.SystemWeb which implements exception handing using both Owin middleware and adding a IExceptionHandler via the httpConfiguration as outlined in this post.

In the below project I've got a controller that throws exceptions with endpoints for both Get and Post. When creating a get request I get the expected response from the Owin exception middleware;

enter image description here

However when making a post request the middleware is skipped and the following returned;

enter image description here

It seems the post request skips the middleware and returns a 500 before making it to the Owin exception handler. I want to be able to catch the post request exception and log it. Any idea how this should be done? and what causes the different behaviour between post and get?

Example Repo and Code Snippets;

https://github.com/timReynolds/WebApiExceptionDemo

OwinExceptionHandlerMiddleware

public class OwinExceptionHandlerMiddleware
{
    private readonly AppFunc _next;

    public OwinExceptionHandlerMiddleware(AppFunc next)
    {
        if (next == null)
        {
            throw new ArgumentNullException("next");
        }

        _next = next;
    }

    public async Task Invoke(IDictionary<string, object> environment)
    {
        try
        {
            await _next(environment);
        }
        catch (Exception ex)
        {
            try
            {
                var owinContext = new OwinContext(environment);
                HandleException(ex, owinContext);
                return;
            }
            catch (Exception)
            {
                Console.WriteLine("Exception while generating the error response");
            }
            throw;
        }
    }

    private void HandleException(Exception ex, IOwinContext context)
    {
        var request = context.Request;
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        context.Response.ReasonPhrase = "Internal Server Error from OwinExceptionHandlerMiddleware";
    }
}

ExampleExceptionLogger

public class ExampleExceptionLogger : IExceptionLogger
{
    public async Task LogAsync(ExceptionLoggerContext context, CancellationToken cancellationToken)
    {
        await Task.Run(() =>
        {
            Console.WriteLine($"Example Exception Logger {context}");
        });
    }
}

Startup

public void Configuration(IAppBuilder appBuilder)
{
    var httpConfiguration = new HttpConfiguration();

    httpConfiguration.Services.Replace(typeof(IExceptionHandler), new ExampleExceptionHandler());
    httpConfiguration.Services.Add(typeof(IExceptionLogger), new ExampleExceptionLogger());

    httpConfiguration.MapHttpAttributeRoutes();
    httpConfiguration.EnableCors();

    appBuilder.UseOwinExceptionHandler();
    appBuilder.UseWebApi(httpConfiguration);
}

Solution

  • Turns out this is caused by using the incorrect Cors package. When hosting via IIS then the EnableCors configuration should be used but inside Owin the Owin specific Cors package should be used instead.

    So to get this to work I removed Microsoft.AspNet.WebApi.Cors and used Microsoft.Owin.Cors instead and made the following changes to the appBuilder;

    public void Configuration(IAppBuilder appBuilder)
    {
        var httpConfiguration = new HttpConfiguration();
    
        httpConfiguration.Services.Replace(typeof(IExceptionHandler), new ExampleExceptionHandler());
        httpConfiguration.Services.Add(typeof(IExceptionLogger), new ExampleExceptionLogger());
    
        httpConfiguration.MapHttpAttributeRoutes();
        // httpConfiguration.EnableCors();
    
        appBuilder.UseOwinExceptionHandler();
        appBuilder.UseCors(CorsOptions.AllowAll); // Use Owin Cors
        appBuilder.UseWebApi(httpConfiguration);
    }
    

    Details on implementing this are summarised here.