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

ASP.NET Web API HttpContext Response is sent back before IOwinContext Response


We are using Owin middleware in an ASP.NET Web API 2 project hosted in IIS.

I am currently experiencing a strange phenomenon where the IOwinContext.Response.Body is not being written to, and actually, even when I have a break point set in the middleware after awake Next.Invoke(), and it gets hit, the response has already been sent back to the server even if I haven't continued yet.

When I look at the response body on the IOwinContext it is empty. However, I can get the response from the HttpContext.Response.Filter. When I use the HttpContext and hit the break point, then the response isn't sent back until I continue. Below is the current configuration method being used in our Startup.cs class.

public async void Configuration(IAppBuilder app)
{
    try
    {
        // Global Config
        var config = GlobalConfiguration.Configuration;

        // configure dependency injection
        UnityConfig.RegisterComponents();

        // configure log for net
        log4net.Config.XmlConfigurator.Configure();

        // turn around all requests right here
        app.Use(async (context, next) =>
        {
            if (context.Request.Path.ToString() == "/")
            {
                string text = "UP";
                context.Response.StatusCode = 200;
                context.Response.ReasonPhrase = text;
                await context.Response.WriteAsync(text);
                return;
            }

            await next.Invoke();
        });

        // Handle exceptions in the OWIN layer here
        app.UseUncaughtExceptionHandler();

        // add cors headers
        app.Use(async (context, next) => { });

        // some UI stuff
        app.Use(async (context, next) => { });

        // Log Request Metrics
        app.UseLogRequestMetrics();

        // Evaluate Partner Key
        app.MapWhen(context => Regex.IsMatch(context.Request.Uri.PathAndQuery.ToLower(), @"/api"), newApp =>
        {
#if !DEBUG
            newApp.Use<Middleware1>();
#endif
            newApp.Use<Middleware2>();

            newApp.Use<Middleware3>(); // On the response path back, the IOwinResponse body is already empty
        });

        WebApiConfig.Register(config);

        app.UseWebApi(config); // It seems like I'm losing the response in here, but I don't really know

        config.EnsureInitialized();

        // Configure object mapping
        AutoMapperConfig.Configure();
    }
    catch (Exception ex)
    {
        await LogForNetErrorLogger.LogError(ex);
    }
}

I'm pretty sure my middleware is messed up, but the response is already gone before it gets back to the first of my middlewares (Middleware3) after the await Next.Invoke()

Any insight or thought provoking would be appreciated. Also, if this isn't enough information please let me know.


Solution

  • So, as in my post above, the problem, I thought, was the HttpResponse was being sent back before the IOwinResponse was. As it turns out, I completely overlooked the mapping section:

    app.MapWhen(context => Regex.IsMatch(context.Request.Uri.PathAndQuery.ToLower(), @"/api"), newApp =>
    {
    #if !DEBUG
        newApp.Use<Middleware1>();
    #endif
        newApp.Use<Middleware2>();
    
        newApp.Use<Middleware3>();
    });
    

    When you use app.Map() it branches the middleware. So, if the path matched "/api" it would branch. However, it was also still using the app.UseWebApi() component so that was why I had two different responses and why the response I was expecting wasn't written to the Middleware3 component's IOwinContext. I fixed it by removing the app.MapWhen() method, changing it from this:

    app.MapWhen(context => Regex.IsMatch(context.Request.Uri.PathAndQuery.ToLower(), @"/site"), newApp =>
    {
    #if !DEBUG
        newApp.Use<Middleware1>();
    #endif
        newApp.Use<Middleware2>();
    
        newApp.Use<Middleware3>(); // On the response path back, the IOwinResponse body is already empty
    });
    

    to this:

    #if !DEBUG
    newApp.Use<Middleware1>();
    #endif
    newApp.Use<Middleware2>();
    
    newApp.Use<Middleware3>();
    

    and putting this piece of code at the beginning of the middleware components Middleware1, Middleware2, Middleware3:

    public override async Task Invoke(IOwinContext context)
    {
        if (!context.Request.Path.ToString().StartsWith("/api/"))
        {
            await Next.Invoke(context);
    
            return;
        }
    
        // stuff I want to run if the above doesn't match
        await Next.Invoke(context);
    
        ...
    }
    

    Well, at least the fix was simple, even if it took me three weeks to find it. If you want to read up on the IAppBuilder.MapWhen extension method, here is some documentation https://msdn.microsoft.com/en-us/library/owin.mapwhenextensions.mapwhen(v=vs.113).aspx.