Skip to content

HttpContext.Current becomes null when an async OnActionExecutingAsync Web API filter runs (Autofac.WebApi2 6.1.0). Works on 6.0.1 #72

@pulkit7sharma

Description

@pulkit7sharma

Describe the Bug

In a .NET Framework 4.7.2 MVC/Web API OWIN application using Autofac 6.5.0 and Autofac.WebApi2 6.1.0, HttpContext.Current is null inside the controller action when an async Web API action filter (IAutofacActionFilter) uses await in OnActionExecutingAsync.

  • If the filter’s OnActionExecutingAsync contains no awaits (i.e., purely sync), HttpContext.Current is available.
  • If I downgrade to Autofac.WebApi2 6.0.1, HttpContext.Current remains available even when the filter is async.

This looks like a regression/change in behavior between 6.0.1 → 6.1.0.

Steps to Reproduce

Reproduction Steps

  1. Create a .NET Framework 4.7.2 “ASP.NET Web Application (.NET Framework)” with MVC + Web API.

  2. Install NuGet packages:

      -       Autofac 6.5.0
      -       Autofac.WebApi2 6.1.0
      -       Autofac.WebApi2.Owin 6.0.0
      -       Autofac.Owin 6.0.1
      -       Microsoft.Owin 4.2.3
      -       Microsoft.Owin.Host.SystemWeb 4.2.3
      -       Owin 1.0
    
  3. Register Autofac and the Web API filter in OWIN Startup.

  4. Add a Web API controller ValuesController and apply the Autofac Web API filter to it (controller or action level).

  5. In the filter’s OnActionExecutingAsync, include at least one await (e.g., await Task.Yield() or any real async call).

  6. Inside the controller action, read HttpContext.Current (or anything that uses it).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Reflection;
using System.Threading;
using System.Web.Http;
using System.Web.Http.ExceptionHandling;
using Autofac;
using Autofac.Integration.WebApi;
using Microsoft.Owin;
using Microsoft.Owin.Logging;
using Newtonsoft.Json.Serialization;
using Owin;
using TestReproApp.Controllers;

[assembly: OwinStartup(typeof(TestReproApp.Startup))]
namespace TestReproApp
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            var builder = new ContainerBuilder();

            // Get your HttpConfiguration.
            var config = new HttpConfiguration();
            config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
            config.MapHttpAttributeRoutes();

            var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().FirstOrDefault();
            if (jsonFormatter != null)
            {
                jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            }

            // Register your Web API controllers.
            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

            // Register the Autofac filter provider.
            builder.RegisterWebApiFilterProvider(config);

            builder.RegisterType<ActionFilter>()
                .WithParameter(new NamedParameter("name", "initial"))
                .AsWebApiActionFilterFor<ValuesController>()
                .InstancePerRequest();

            var container = builder.Build();
            var dependencyResolver = new AutofacWebApiDependencyResolver(container);
            config.DependencyResolver = dependencyResolver;

            // Register the Autofac middleware FIRST, then the Autofac Web API middleware,
            // and finally the standard Web API middleware.
            app.UseAutofacMiddleware(container);
            app.UseAutofacWebApi(config);
            app.UseWebApi(config);

            // By default the IncludeErrorDetail policy is only for Local. Overriding the value to get the details always.
            // ref: https://blogs.msdn.microsoft.com/youssefm/2012/06/28/error-handling-in-asp-net-webapi/
            config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;

            config.EnsureInitialized();
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using Autofac.Integration.WebApi;

namespace TestReproApp.Controllers
{
    public class ActionFilter : IAutofacActionFilter
    {
        public ActionFilter(string name)
        {
            
        }

        public async Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
        {
            try
            {
                var x = new HttpContextWrapper(System.Web.HttpContext.Current).CurrentHandler;
                Console.WriteLine("Action executed.");
                //throw new NotImplementedException();
                await Task.Delay(3000).ConfigureAwait(false);
                return;
            }
            catch (Exception ex) { }
        }

        public async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
        {
            try
            {
                var x = new HttpContextWrapper(System.Web.HttpContext.Current).CurrentHandler;
                Console.WriteLine("Action executing.");
                await Task.Delay(3000).ConfigureAwait(false);
                return;
            }
            catch(Exception ex) { }
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Web;
using System.Web.Http;

namespace TestReproApp.Controllers
{
    [RoutePrefix("Help/Api")]
    public class ValuesController : ApiController
    {
        // GET api/values
        [Route("GET-api-Values")]
        public IEnumerable<string> Get()
        {
            using (var cancellationTokenSource = new System.Threading.CancellationTokenSource(3000))
            {
               // Null HttpContext.Current
               var x = new HttpContextWrapper(System.Web.HttpContext.Current).CurrentHandler;
               Thread.Sleep(4000); // Simulate some work
               return new string[] { "value1", "value2" };
            }
        }

        // GET api/values/5
        public string Get(int id)
        {
            return "value";
        }

        // POST api/values
        public void Post([FromBody] string value)
        {
        }

        // PUT api/values/5
        public void Put(int id, [FromBody] string value)
        {
        }

        // DELETE api/values/5
        public void Delete(int id)
        {
        }
    }
}

Expected Behavior

HttpContext.Current is non-null in the controller action.

Exception with Stack Trace

Put the exception with stack trace here.

Dependency Versions

     -   Autofac 6.5.0
     -       Autofac.WebApi2 6.1.0
     -       Autofac.WebApi2.Owin 6.0.0
     -       Autofac.Owin 6.0.1

Additional Info

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions