Giter VIP home page Giter VIP logo

autofac.webapi's Introduction

Autofac.WebApi

ASP.NET Web API integration for Autofac.

Build status

Please file issues and pull requests for this package in this repository rather than in the Autofac core repo.

Quick Start

To get Autofac integrated with Web API you need to reference the Web API integration NuGet package, register your controllers, and set the dependency resolver. You can optionally enable other features as well.

protected void Application_Start()
{
  var builder = new ContainerBuilder();

  // Get your HttpConfiguration.
  var config = GlobalConfiguration.Configuration;

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

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

  // OPTIONAL: Register the Autofac model binder provider.
  builder.RegisterWebApiModelBinderProvider();

  // Set the dependency resolver to be Autofac.
  var container = builder.Build();
  config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}

Check out the documentation for more usage details.

Get Help

Need help with Autofac? We have a documentation site as well as API documentation. We're ready to answer your questions on Stack Overflow or check out the discussion forum.

autofac.webapi's People

Contributors

alexandrnikitin avatar alexmg avatar alistairjevans avatar alsami avatar dependabot[bot] avatar dmorganb avatar pengweiqhca avatar shiftkey avatar srogovtsev avatar tillig avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

autofac.webapi's Issues

Help - Autofac.WebApi2 Global Action FIlters Parameters injection failed

Hi all,

I'm facing an issue with some global ActionFilters where I'm expecting to get injected a dependency that is not happening.

It is probably because I'm missing something, but I can find what.
Here is the StackOverflow question I posted.
Do you guys want me to include all the description here as well?

Thanks.

Test Failures on Autofac 4.0.1 or Later

Autofac 4.0.1 changed the order of results when resolving an IEnumerable of registrations to be registration order rather than reverse registration order.

Currently the WebApi tests fail if they are run against a later version of Autofac than the one we build against at the moment (3.5.2), because of filter order assertions.

There isn't actually an issue with the integration (which works as intended), it just makes the test results invalid if we do an upgrade test on this integration.

Proposal is to upgrade the Autofac version this integration depends on to 4.0.1 and update the tests that assert on order.

RegisterHttpRequestMessage fails with Azure App Service Mobile App

From @Louliloul on June 22, 2015 16:8

Hi !

I'm currently playing with Azure App Services Mobile App. I set up a basic solution in VS 2015 RC with Autofac as DI, and one of my dependencies requires the current HttpRequestMessage. So i used the extensions RegisterHttpRequestMessage in order to be able to inject it as needed. But the call to RegisterHttpRequestMessage produces the following exception:

Exception Details: System.TypeAccessException: Attempt by security transparent method 'Autofac.Integration.WebApi.RegistrationExtensions.RegisterHttpRequestMessage(Autofac.ContainerBuilder, System.Web.Http.HttpConfiguration)' to access security critical type 'System.Web.Http.HttpConfiguration' failed.

Assembly 'Autofac.Integration.WebApi, Version=3.0.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da' is marked with the AllowPartiallyTrustedCallersAttribute, and uses the level 2 security transparency model. Level 2 transparency causes all methods in AllowPartiallyTrustedCallers assemblies to become security transparent by default, which may be the cause of this exception.

As the call to RegisterHttpRequestMessage comes before the call to the concerned module, my controller instanciation fails. The second breakpoint below is never reached.
image

And when i try to access the service homepage, it tells that the service is unhealthy.
image

Copied from original issue: autofac/Autofac#649

Can't add multiple filters to the same controller

From @taschmidt on October 15, 2014 13:53

Given this code:

    builder.RegisterType<FilterA>().AsWebApiExceptionFilterFor<MyController>().InstancePerRequest();
    builder.RegisterType<FilterB>().AsWebApiExceptionFilterFor<MyController>().InstancePerRequest();

I get the following exception:

[ArgumentException: An item with the same key has already been added.]
   System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add) +14874155
   Autofac.Builder.RegistrationBuilder`3.WithMetadata(String key, Object value) +38
   Autofac.Integration.WebApi.RegistrationExtensions.AsFilterFor(IRegistrationBuilder`3 registration, String metadataKey) +294
   ...

We should definitely have the ability to register multiple filters on the same controller.

Copied from original issue: autofac/Autofac#586

Web API filters using actionSelectors not working properly with derived controllers

From @alexmg on January 22, 2014 14:29

From [email protected] on October 25, 2013 01:59:28

What steps will reproduce the problem?

  1. An API controller with action methods
  2. A derived API controller which is called based on the routing
  3. An action filter registered using AsWebApiAuthorizationFilterFor where T can be the base or derived controller and a lambda pointing to an action method that exists on this base controller

What is the expected output? What do you see instead?
Expect that the actionfilter will be called when accessing the resource, but it is not called.

This is an odd case possibly related to some reflection being done by Autofac, this case only occurs if the action method exists in the base controller and is called via the derived controller but goes away if the method is marked as virtual (regardless of if the derived controller implements an override or not).

Original issue: http://code.google.com/p/autofac/issues/detail?id=463

Copied from original issue: autofac/Autofac#463

System.IO.FileLoadException when loading Autofac.Integration.WebApi

I switched from Ninject to Autofac today because Ninject's InRequestScope didn't work well in Owin.

However, I immediately encountered a problem. The code compiles just fine but will throw an exception during run-time:

初始化方法 InRequestScopeOwinAutofac.Tests.Controllers.ValuesControllerTest.Init 引发异常。System.Reflection.TargetInvocationException: System.Reflection.TargetInvocationException: 调用的目标发生了异常。 ---> System.IO.FileLoadException: 未能加载文件或程序集“Autofac.Integration.WebApi, Version=3.0.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da”或它的某一个依赖项。找到的程序集清单定义与程序集引用不匹配。 (异常来自 HRESULT:0x80131040)。

This problem is similar to the issue: #455 except it is slightly different because it is about another library.

I googled for a few fixes but none of them worked. Things I tried so far:

  1. Update .net version (following http://www.paraesthesia.com/archive/2013/03/29/portable-class-library-answers.aspx/)
  2. Add bindRedirect (following autofac/Autofac#587)

I updated all packages in the solution to their latest releases and it didn't help either.

Or is it the same problem again as you once answered to here: http://stackoverflow.com/questions/21390740/force-a-3rd-party-assembly-to-use-another-version-of-another-assembly?

I prepared a minimal project and pushed to this repo: https://github.com/foresightyj/InRequestScopeOwinAutofac

Any help is appreciated.

RegisterWebApiModelBinders has no effect in Web API 2

From @alexmg on January 22, 2014 14:32

From [email protected] on January 20, 2014 00:15:41

Registering Web API 2 model binders using the extension method RegisterWebApiModelBinders has no effect. It's missing the required metadata needed by AutofacWebApiModelBinderProvider to resolve the model binder.

Instead of using RegisterWebApiModelBinders, one can manually register the model binders, using:
builder.RegisterType<RequestContextModelBinder>().AsModelBinderForTypes(typeof(RequestContext));

Not sure if it is possible to make RegisterWebApiModelBinders work, otherwise it should probably be deprecated for Web API 2.

Original issue: http://code.google.com/p/autofac/issues/detail?id=484

Copied from original issue: autofac/Autofac#484

RegisterHttpRequestMessage requires a ContainerBuilder but doesn't use it

All this method really needs is an HttpConfiguration object, and as such it should really be an extension method on HttpConfiguration, similar to MapHttpAttributeRoutes or other built-in extension methods, not an extension method on ContainerBuilder, which it doesn't use.

Also, because it doesn't use ContainerBuilder, there's no meaningful guidance on what container builder to call it on. I'm currently calling it on a throwaway instance just to avoid the argument null exception.

InstancePerRequest does not work as expected with http batch requests

After implementing http batch handling (with non sequential execution) in web api like described here my dependencies setup with InstancePerApiRequest do not act as expected.

Each of the sub-requests of the batch will get the same instance of the class setup with InstancePerApiRequest, which is problematic when dealing with database transactions, because the sub-requests come in on different threads at the same time.

Is there any way to fix this or work around it?

Thanks in advance for any help!

AsWebApiAuthorizationFilterOverrideFor does not overrride

Here ist my default Authorizations filter:

builder.RegisterType<IsLoggedInAuthorizationFilter>()
  .AsWebApiAuthorizationFilterFor<BroadcastsController>()
  .InstancePerRequest();

This works fine, but if I want to overrride the Filter to allow anonymous requests like this:

builder.RegisterType<AnonymousAuthorizationFilter>()
  .AsWebApiAuthorizationFilterOverrideFor<BroadcastsController>()
  .InstancePerRequest();

Both get called:

  1. AnonymousAuthorizationFilter
  2. IsLoggedInAuthorizationFilter

I would expect that only AnonymousAuthorizationFilter gets called.

Is that the intended behavior, or am I'm doing something wrong?

There is literally no documentions for AsWebApiAuthorizationFilterOverrideFor and I cannot find anything on the web.

Change IAutofacActionFilter and similar signatures to Task instead of void.

From @alexmg on January 22, 2014 14:21

From [email protected] on February 28, 2013 13:06:35

What steps will reproduce the problem?

  1. Create and implementation of IAutofacActionFilter
  2. On the implementation of OnActionExecuting decorate it with async
  3. Create a test case for the OnActionExecuting method.
  4. The test completes because I cannot await a void method. What is the expected output? What do you see instead? If there is either an async version of IAutofacActionFilter or it returns a task testing would be possible. The code is behaving as expected I just can't write tests for it. What version of Autofac are you using? On what version of .NET/Silverlight? Autofac 3.0.0 Please provide any additional information below. I can`t use a common IActionFilter because of a requirement to use an component that is InstancePerApiRequest().

Original issue: http://code.google.com/p/autofac/issues/detail?id=411

Copied from original issue: autofac/Autofac#411

What should I do tp set the value of ControllerContext property after GetService method?

Hi team,
I came across an idea to get an controller instance in another controller.
First, I found the following answer:
https://stackoverflow.com/questions/16870413/how-to-call-another-controller-action-from-a-controller-in-mvc/32098348#32098348

var controller = DependencyResolver.Current.GetService();
controller.ControllerContext = new ControllerContext(this.Request.RequestContext, controller);

 And I try to implement it with Autofac.WebApi2 like this:
            var dependencyResolver = GlobalConfiguration.Configuration.DependencyResolver;
            var controller = dependencyResolver.GetService(typeof(ProductsController));
            ProductsController productsController = controller as ProductsController;
            //productsController.ControllerContext //might need set value here

I found one constructor have parameter matches context,
public HttpControllerContext(HttpRequestContext requestContext, HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, IHttpController controller);
But I have no idea about what I should pass for HttpRequestMessage and HttpControllerDescriptor parameters.

Do you have any suggestion?

Enable WebAPI action filters to be applied based on predicate

From @alexmg on January 22, 2014 14:25

From travis.illig on May 29, 2013 04:38:55

The idea is to enable additional configuration of when a filter applies to a controller by allowing use of a lambda predicate during registration.

builder....AsWebApiActionFilter(
methodInfo => methodInfo.DeclaringType.Namespace.Contains(".Foo") &&
methodInfo.GetAttribute() != null)

Code/discussion here: https://groups.google.com/forum/?fromgroups#!msg/autofac/S2ia55LBqXo/ZnPNvb3UVyMJ

Original issue: http://code.google.com/p/autofac/issues/detail?id=439

Copied from original issue: autofac/Autofac#439

OWIN/ WebApi2 nested lifetimescope exception

From @tynor88 on September 12, 2016 9:11

Hi

Having followed the WebApi2 + OWIN integration documentation, I am facing an exception I cannot figure out how to solve.

Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed.

Stacktrace:
[ObjectDisposedException: Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed.] Autofac.Core.Lifetime.LifetimeScope.CheckNotDisposed() +67 Autofac.Core.Lifetime.LifetimeScope.BeginLifetimeScope(Object tag, Action1 configurationAction) +30 Owin.<<RegisterAutofacLifetimeScopeInjector>b__0>d.MoveNext() +200 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13908768 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61 Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.<RunApp>d__5.MoveNext() +203 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13908768 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61 Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.<DoFinalWork>d__2.MoveNext() +193 Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.StageAsyncResult.End(IAsyncResult ar) +96 System.Web.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +363 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +137

I am not creating any new lifetime scopes myself. I've created a sample project which is similar to my current setup.

It's difficult for me to debug, as this exception only happends when the site is deployed to a Virtual Machine (Windows Server 2012) running IIS 8.5. The exception occurs randomly whenever I hit either the WebApi2 endpoint or the OwinMiddleware HelloWorld endpoint. I even tried the Remote Debugging tools, and whenever I remote debug, the OWIN / WebApi2 endpoints work just fine.

It seems like the issue is around line 35 of the code in Startup.cs class:

        public void Configuration(IAppBuilder app)
        {
            var config = new HttpConfiguration();
            IContainer container = Bootstrapper.Bootstrap;

            ILogger logger = container.Resolve<ILogger>();

            try
            {
                config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
                app.UseAutofacMiddleware(container);

                app.UseAutofacWebApi(config);
                app.UseWebApi(config);

                config.MapHttpAttributeRoutes();

                //This line seems to give me some problems - If i don't call Foo() here, everything is working normally.
                //Foo is just manipulating a configuration file - services injected into it are just Singleton's.
                container.Resolve<IService>().Foo();
            }
            catch (Exception ex)
            {
                logger.Error(ex, "Unhandled exception occured");
                throw;
            }
        }
`

_Copied from original issue: autofac/Autofac#795_

Breaking Changes in 4.0.0?

Hi. Are there any breaking changes in you 4.0.0 (autofac, autofac.owin, autofac.webapi2, autofac.webapi2.owin) releases in comparison to 3.4.0?

Regards
Novak

Null Reference with a combination of ODataController and RegisterWebApiFilterProvider when starting the app in the Owin pipeline.

From @SpiegelSoft on January 24, 2015 19:3

This is similar to Issue autofac/Autofac#561, but I think I have an accurate way to reproduce it.

Using Autofac.WebApi2, Version 3.4.0.

Because of the rules around SignalR integration, it is recommended to set up the container in the Owin startup pipeline.

Create a new web project, and add Microsoft.Owin.Host.SystemWeb.

Add a new file, called Startup.cs, containing the following content:

using System.Reflection;
using System.Web.Http;
using Autofac;
using Autofac.Integration.WebApi;
using Microsoft.Owin;
using Owin;
using SpiegelSoftUsers;

[assembly: OwinStartup(typeof(Startup))]

namespace MyWebApp
{
public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterApiControllers(Assembly.GetExecutingAssembly());
var config = GlobalConfiguration.Configuration;
containerBuilder.RegisterWebApiFilterProvider(config);
var container = containerBuilder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
}
}

Run the web app, and navigate to /odata.

The result is

{
"error":{
"code":"","message":"An error has occurred.","innererror":{
"message":"Object reference not set to an instance of an object.","type":"System.NullReferenceException","stacktrace":" at Autofac.Integration.WebApi.AutofacWebApiFilterProvider.GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor)\r\n at System.Web.Http.Controllers.HttpActionDescriptor.b__0(IFilterProvider fp)\r\n at System.Linq.Enumerable.d__142.MoveNext()\r\n at System.Linq.Buffer1..ctor(IEnumerable1 source)\r\n at System.Linq.OrderedEnumerable1.d__0.MoveNext()\r\n at System.Linq.Buffer1..ctor(IEnumerable1 source)\r\n at System.Linq.Enumerable.d__a01.MoveNext()\r\n at System.Web.Http.Controllers.HttpActionDescriptor.<RemoveDuplicates>d__3.MoveNext()\r\n at System.Linq.Buffer1..ctor(IEnumerable1 source)\r\n at System.Linq.Enumerable.<ReverseIterator>d__a01.MoveNext()\r\n at System.Collections.Generic.List1..ctor(IEnumerable1 collection)\r\n at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source)\r\n at System.Web.Http.Controllers.HttpActionDescriptor.InitializeFilterPipeline()\r\n at System.Lazy1.CreateValue()\r\n at System.Lazy1.LazyInitValue()\r\n at System.Lazy1.get_Value()\r\n at System.Web.Http.Controllers.HttpActionDescriptor.GetFilterPipeline()\r\n at System.Web.Http.Controllers.HttpActionDescriptor.GetFilterGrouping()\r\n at System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()"
}
}
}

If you move all of that setup code into WebApiConfig, the error goes away.

Copied from original issue: autofac/Autofac#617

Web requests never return when using Autofac, unless pre-resolved

Describe the Bug

I am having an issue resolving services when using this package. My services all resolve correctly, except through the controllers registered with Autofac. When I make a web request, the request never responds and I get no error messages or exceptions being thrown. I AM able to get everything working if I manually request the service right after building the container in the application start method.

Steps to Reproduce

public class Global : HttpApplication
{
    private void Application_Start(object sender, EventArgs e)
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<MyService>().As<IMyService>();
        builder.RegisterApiControllers(typeof(WebApiConfig).Assembly).InstancePerRequest();
        IContainer container = builder.Build();

        // When this is uncommented, everything works fine
        //var svc = container.Resolve<IMyService>();

        GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(ContainerProvider.ApplicationContainer);
    }
}

Expected Behavior

I should either have my service resolved properly, or have an exception thrown.

Exception with Stack Trace

Unfortunately hard to debug without an exception.

Dependency Versions

Autofac: v5.2.0
Autofac.Web: v5.0.0
Autofac.WebApi2: v5.0.0

Additional Info

I simplified the dependencies for sake of example but the full dependency hierarchy looks something like this:

  • MyController (InstancePerRequest)
    • IMyService (InstancePerDependency)
      • ISubService (SingleInstance)

ASP.NET Web API (4.6) property injection into filters

From @brgrz on February 15, 2017 12:12

I'm having an issue where I'm doing property injection within filters/attributes.

The issue is that filters registered through

config.Filters.Add(new MetadataDecoratorAttribute());

do not get their public properties injected

public MyEntities DbContext { get; set; }

while if I apply those filters to the controllers (or, in my case to our base api controller) like this

[MetadataDecorator]
public class ApiControllerBase : ApiController

then property injection does happen.

I am using the filter registration as instructed

// register DI for Web API attributes (action filters)
builder.RegisterWebApiFilterProvider(GlobalConfiguration.Configuration);

However the filters in question do not implement Autofac's IAutofacActionFilter because that one cannot be used in config.Filters.Add() but they do inherit the ActionFilterAttribute class.

For now, we have reverted to attaching the filters to the base ctrl class but I think this is a bug that should be resolved/explained because adding throughconfig.Filters.Add()should also work.

Copied from original issue: autofac/Autofac#831

RegisterApiControllers | Filters `IHttpController` with `Controller` suffix

Hello Team,

according to the doc

Register types that implement IHttpController

but actually it also filters the controllers by the suffix. And we override the contract with

var suffix = typeof(DefaultHttpControllerSelector).GetField(
       "ControllerSuffix", BindingFlags.Static | BindingFlags.Public
);
suffix?.SetValue(null, "Endpoint");

So the method doesn't registers our class FooEndpoint: ApiControllers:

return builder.RegisterAssemblyTypes(controllerAssemblies)

Please update the doc, or give the possibility to configurate the suffix filter. Thank you!

Cannot get Autofac to work with Web API

I am struggling to get AutoFac to work with WebApi2 controllers

I always get an error that there is no parameterless constructor


    {
        "Message": "An error has occurred.",
        "ExceptionMessage": "An error occurred when trying to create a controller of type 'GatewayController'. Make sure that the controller has a parameterless public constructor.",
        "ExceptionType": "System.InvalidOperationException",
        "StackTrace": "   at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)\r\n   at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)\r\n   at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__15.MoveNext()",
        "InnerException": {
            "Message": "An error has occurred.",
            "ExceptionMessage": "Type 'Gv8ApiGateway.Controllers.GatewayController' does not have a default constructor".....
        }
    }

I have been through loads of posts on this online and I cant see that I have missed anything

When I look at my container after it has been built I can see that it does contain my controller

I am using TopShelf


        HostFactory.Run(x => //1
        {
            x.UseAutofacContainer(container);

            x.Service<IMyService>(s => //2
            {
                s.ConstructUsingAutofacContainer();
                s.WhenStarted(tc => tc.Start());
                s.WhenStopped(tc => tc.Stop());
            });
            x.SetStartTimeout(TimeSpan.FromMinutes(4));
            x.StartAutomatically();
            x.RunAsLocalSystem();
            x.EnableServiceRecovery(r => { r.RestartService(0); });
            x.SetDescription($"DESCRIPTION");
            x.SetDisplayName($"DISPLAY NAME");
            x.SetServiceName($"NAME");
        });

In my assembly module I have the line which I have verified is being called

builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

In my class that is started by TopShelf I have -


    var resolver = new AutofacWebApiDependencyResolver(_container);
    GlobalConfiguration.Configuration.DependencyResolver = resolver;
    _webApi = WebApp.Start<Startup>("http://localhost:8084");

My Startup class is -


    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // Configure Web API for self-host. 
            var config = new HttpConfiguration();

            config.MapHttpAttributeRoutes();

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

            app.UseWebApi(config);


        }
    }

I think the key is the place that I set the dependency resolver but it doesnt seem to make any difference where I call this, i.e. before or after I start the API

My controller is:


    public class GatewayController : ApiController
    {
        private readonly IMyService_myService;
        
        public GatewayController(IMyService myService)
        {
            Argument.IsNotNull(() => myService);

            _myService = myService;
        }    
    }

Can anyone see what I have done wrong please?

Paul

Could not load file or assembly 'Autofac, Version=3.0.0.0' when using Autofac.WebApi 3.1.0

When i am using

id="Autofac" version="4.6.1" targetFramework="net462" />
id="Autofac.WebApi" version="3.1.0" targetFramework="net462" />
id="Steeltoe.CloudFoundry.ConnectorAutofac" version="2.0.0" targetFramework="net462" />

in VS2017, with .net framework 4.6.2, getting below error in runtime,
Could not load file or assembly 'Autofac, Version=3.0.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

Self-hosted WebApi Custom Authorization Filter/Attribute using IAutofacAuthorizationFilter

Hello,

I've read the Autofac Filter DI documentation several times. There is something I'm missing or not understanding.

I've created a authorization filter :

public class CustomAuthorizationAttribute : AuthorizeAttribute,  IAutofacAuthorizationFilter
    {
        private SettingsManager _settingsManager;       
        public CustomAuthorizationAttribute(SettingsManager settingsManager)
        {           
            _settingsManager = settingsManager;
        }


        public override Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
        {          
            //do some checking?
            actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "You are not authorized.");
            return Task.FromResult(0);
        }
    }

I wired it up in my Startup.cs like this:


           var builder = new ContainerBuilder();

            HttpListener listener =
            (HttpListener)appBuilder.Properties["System.Net.HttpListener"];
            listener.AuthenticationSchemes =
            AuthenticationSchemes.IntegratedWindowsAuthentication | AuthenticationSchemes.Anonymous;

            // Configure Web API for self-host. 
            HttpConfiguration config = new HttpConfiguration(); 
               
            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());      
            builder.RegisterWebApiFilterProvider(config);

            builder.RegisterType<CustomAuthorizationAttribute>()
             .AsWebApiAuthorizationFilterFor<MyController>(c => c.MyMethod(default(string)))
             .InstancePerRequest();

            builder.RegisterType<SettingsManager>();        

            var container = builder.Build();
            config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
          
            appBuilder.UseAutofacMiddleware(container);
            appBuilder.UseAutofacWebApi(config);
            appBuilder.UseWebApi(config);

I presumed the above would call my Custom filter every time the method was called in my controller. It did not. It ignored my filter.

I tried adding [Authorize] to MyMethod, but that would result in an immediate 401, and never step into my filter.

I tried adding [CustomAuthorize] to MyMethod, that would trigger my filter, but I can't have constructor with parameters using the .NET attribute classes. This pushes me towards using the service locator inside of CustomAuthorize attribute instead of the runtime injection.

So I have 2 question:

  1. Does my code above look correct?
  2. When would a filter using IAutofacAuthorizationFilter be useful, if the above is not possible. What would trigger it?

Thanks!
Brian

.NET Framework 4.8
Autofac 6.2.0
Autofac OWIN 6.0.1
Autofac.WebApi2 6.0.0
Autofac.WebApi2.Owin 6.0.0

Change CallContext to AsyncLocal

Refactoring HttpRequestMessageProvider, change CallContext to AsyncLocal.

System.Security.SecurityException : Type System.Runtime.Remoting.ObjRef and the types derived from it (such as System.Runtime.Remoting.ObjRef) are not permitted to be deserialized at this security level.

Server stack trace: 
   at System.Runtime.Serialization.FormatterServices.CheckTypeSecurity(Type t, TypeFilterLevel securityLevel)
   at System.Runtime.Serialization.Formatters.Binary.ObjectReader.CheckSecurity(ParseRecord pr)
   at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ParseObject(ParseRecord pr)
   at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Parse(ParseRecord pr)
   at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryObjectWithMapTyped record)
   at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryHeaderEnum binaryHeaderEnum)
   at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run()
   at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   at System.Runtime.Remoting.Channels.CoreChannel.DeserializeBinaryRequestMessage(String objectUri, Stream inputStream, Boolean bStrictBinding, TypeFilterLevel securityLevel)
   at System.Runtime.Remoting.Channels.BinaryServerFormatterSink.ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, Stream requestStream, IMessage& responseMsg, ITransportHeaders& responseHeaders, Stream& responseStream)

Exception rethrown at [0]: 
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at ......

Autofac filters for specific 'async' action in a controller not working

I have a Controller with an async Action. I would like to register a ActionFiler (one that implements IAutofacActionFilter, My code is based on Web API 2, using the Autofac Web API intergration module) for that specific 'async' action in that controller. Following are the snippets I tried and it didn't work. But Synchronous actions do work as mentioned in the Autofac guideline documents.

Tried a few way of specifying the asyn action in the 'Register' configuration but nothing worked. Please refer to the snippets below for tried code.

builder.RegisterWebApiFilterProvider(GlobalConfiguration.Configuration); builder.Register(componentContext => new RequestLatencyTrackerActionFilter()) .AsWebApiActionFilterFor<>((actionSelector) => Task.Run<IHttpActionResult>(() => actionSelector.GetMyActionAsync(default(string))))) .InstancePerApiRequest();

Or,

builder.Register(componentContext => new RequestLatencyTrackerActionFilter()) .AsWebApiActionFilterFor<>((actionSelector) => actionSelector.GetMyActionAsync(default(string)).Wait())) .InstancePerApiRequest();

A 'Non-async' action works,

builder.Register(componentContext => new RequestLatencyTrackerActionFilter()) .AsWebApiActionFilterFor<>((actionSelector) => actionSelector.GetMyNonAsyncAction(default(string)))) .InstancePerApiRequest();

Are registrations for async actions supported in the Autofac Web API integration modules?

Seems like this not tested and the behavior is undefined.
https://stackoverflow.com/questions/58139190/autofac-filters-for-specific-async-action-in-a-controller-not-working

Filters at controller scope not ran when action scoped filters exist.

Describe the Bug

Filters that specify a controller and action are the only ones executed and the ones specified for a controller or for all controllers are not ran. I dont' see anything in the documentation that says this since it says that filters are not removed or replaced.

Steps to Reproduce

[RoutePrefix("api/healthcheck")]
    public class HealthCheckController : ApiController
    {
        [HttpGet]
        [Route]
        public IEnumerable<string> Get()
        {
            //throw new InvalidDataException("TEST");
            return new string[] { "value1", "value2" };
        }
    }

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

            var builder = new ContainerBuilder();

            builder.RegisterType<HealthCheckController>()
                .InstancePerRequest();

            builder.RegisterType<E1>()
                .AsWebApiActionFilterOverrideFor<HealthCheckController>()
                .SingleInstance();

            builder.RegisterType<E2>()
                .AsWebApiActionFilterOverrideForAllControllers()
                .SingleInstance();

            builder.RegisterType<E3>()
                .AsWebApiActionFilterOverrideFor<HealthCheckController>(x => x.Get())
                .SingleInstance();

            builder.RegisterType<E4>()
                .AsWebApiActionFilterFor<HealthCheckController>()
                .SingleInstance();

            builder.RegisterType<E5>()
                .AsWebApiActionFilterForAllControllers()
                .SingleInstance();

            builder.RegisterType<E6>()
                .AsWebApiActionFilterFor<HealthCheckController>(x => x.Get())
                .SingleInstance();

            builder.RegisterWebApiFilterProvider(config);

            builder.RegisterWebApiModelBinderProvider();

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

    public class E1 : IAutofacContinuationActionFilter
    {
        public async Task<HttpResponseMessage> ExecuteActionFilterAsync(
            HttpActionContext actionContext,
            CancellationToken cancellationToken,
            Func<Task<HttpResponseMessage>> next)
        {
            Trace.WriteLine("E1 - continue");
            return await next();
        }
    }

    public class E2 : IAutofacContinuationActionFilter
    {
        public async Task<HttpResponseMessage> ExecuteActionFilterAsync(
            HttpActionContext actionContext,
            CancellationToken cancellationToken,
            Func<Task<HttpResponseMessage>> next)
        {
            Trace.WriteLine("E2 - continue");
            return await next();
        }
    }

    public class E3 : IAutofacContinuationActionFilter
    {
        public async Task<HttpResponseMessage> ExecuteActionFilterAsync(
            HttpActionContext actionContext,
            CancellationToken cancellationToken,
            Func<Task<HttpResponseMessage>> next)
        {
            Trace.WriteLine("E3 - continue");
            return await next();
        }
    }

    public class E4 : IAutofacContinuationActionFilter
    {
        public async Task<HttpResponseMessage> ExecuteActionFilterAsync(
            HttpActionContext actionContext,
            CancellationToken cancellationToken,
            Func<Task<HttpResponseMessage>> next)
        {
            Trace.WriteLine("E4 - continue");
            return await next();
        }
    }

    public class E5 : IAutofacContinuationActionFilter
    {
        public async Task<HttpResponseMessage> ExecuteActionFilterAsync(
            HttpActionContext actionContext,
            CancellationToken cancellationToken,
            Func<Task<HttpResponseMessage>> next)
        {
            Trace.WriteLine("E5 - continue");
            return await next();
        }
    }

    public class E6 : IAutofacContinuationActionFilter
    {
        public async Task<HttpResponseMessage> ExecuteActionFilterAsync(
            HttpActionContext actionContext,
            CancellationToken cancellationToken,
            Func<Task<HttpResponseMessage>> next)
        {
            Trace.WriteLine("E6 - continue");
            return await next();
        }
    }

Expected Behavior

When I run the above code, I get this output in order:

E3 - continue
E6 - continue

and I expect

E1 - continue
E2 - continue
E3 - continue
E4 - continue
E5 - continue
E6 - continue

Dependency Versions

Autofac.WebApi2 6.0.1
Autofac 6.2.0

Weird User.Identity in a InstancePerRequest resolution

From @toben on November 9, 2015 14:34

It's weird, when I am in my ApiController, HttpContext.Current.User.Identity is good. IsAuthenticated is true, name is the username, etc...
But, before that, when I am in a resolution of an "InstancePerRequest" injection, HttpContext.Current.User.Identity is weird and all fields are null or empty...

            builder.Register(c => new HttpContextWrapper(HttpContext.Current))
                .As<HttpContextBase>()
                .InstancePerRequest();

This is the code which inject HttpContext.Current in other injections. How that can be possible ?

Copied from original issue: autofac/Autofac#692

AutofacWebApiModelBinderProvider doesn't work

Model and binder:

    public class MyModel
    {
        public int Number { get; set; }
    }

    public class MyModelBinder : IModelBinder
    {
        public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
        {
            return true;
        }
    }

web api configuration

var builder = new ContainerBuilder();
// Register your Web API controllers.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterWebApiModelBinderProvider();
builder.RegisterType<MyModelBinder>().AsModelBinderForTypes(typeof(MyModel));
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

web api method

        public void Post(MyModel model)
        {
        }

So BindModel method is never called.
Am I doing something wrong?

Filters provided via dependency injection are called in the same order pre- & post-action

If 2 filters A & B are provided for a controller via depedency injection & OnActionExecuting is called for filter A & then filter B, then OnActionExecuted is called for A & then B.

If the same filters are added to HttpConfiguration.Filters then OnActionExecuted is called for B & then A, which I think is the correct order.

Observed using Autofac 3.5.2, Autofac.WebApi2 3.4.0.

IAutofacAuthenticationFilter (HttpContext == null)

Attempting to access HttpContext.Current in a standard IAuthenticationFilter (WebAPI2 / .NET 4.6) will return the appropriate HttpContext object.

However using Autofac.WebAPI2 3.4.0, and attempting to access HttpContext.Current in an injected IAutofacAuthenticationFilter will result in null.

Please, consider to release the 4.2.0 version of the Nuget package

Hello guys, thank you for this very useful library. We would like to use the new version since it has very important feature - GetService/GetServices methods are virtual.

We were able to deal without this feature by creating a new implementation of IDependencyResolver and aggregating an instance of AutofacWebApiDependencyResolver but today we have faced with the following: RegisterWebApiFilterProvider we wanted to use today seriously relies on the fact that HttpConfiguration.DependencyResolver is a subclass of AutofacWebApiDependencyResolver (please, see DependencyResolverExtensions.GetRootLifetimeScope) so our custom implementation of IDependencyResolver does not work for RegisterWebApiFilterProvider.

In case that methods are virtual we no longer need in aggregating AutofacWebApiDependencyResolver and will just inherit from it in order to override virtual methods. And that check in DependencyResolverExtensions.GetRootLifetimeScope will not break.

Performance: Use TryGetValue instead of ContainsKey() + indexer sequence

No biggie but this should really use Dictionary.TryGetValue for performance reasons:

var metadata = filter.Metadata.ContainsKey(this.MetadataKey)
? filter.Metadata[this.MetadataKey] as FilterMetadata : null;

image

Admittedly, the profiler session was using the "Tracing" configuration so we should expect an overemphasis on small methods but then again, this change should be a safe no-brainer.

Suggestion: Alternative solution for injecting HttpRequestMessage

The current implementation of injecting HttpRequestMessage relies on updating the component registry

internal static void UpdateScopeWithHttpRequestMessage(HttpRequestMessage request)
{
    var scope = request.GetDependencyScope();
    var requestScope = scope.GetRequestLifetimeScope();
     if (requestScope == null) return;

     var registry = requestScope.ComponentRegistry;
     var builder = new ContainerBuilder();
     builder.Register(c => request).InstancePerRequest();
     builder.Update(registry);
}

https://github.com/autofac/Autofac.WebApi/blob/develop/src/Autofac.Integration.WebApi/CurrentRequestHandler.cs#L66

However this defeats the best practices in the documentation: the container should be considered immutable.

Also ContainerBuilder.Update is marked as obsolete in this commit autofac/Autofac@8a89e94

Should another approach be considered? Like the one Simple Injector is using, by capturing the HttpRequestMessage in a provider:
https://github.com/simpleinjector/SimpleInjector/blob/master/src/SimpleInjector.Integration.WebApi/SimpleInjectorWebApiExtensions.cs

I will be happy to help with a pull request if needed.

DependencyResolutionException thrown when resolving through a delegate returning a .NET Standard 2.0 type

Summary

Autofac.Core.DependencyResolutionException is thrown when resolving for a component through a delegate that returns a type defined in a .NET Standard 2.0 library, under the context of an classic ASP.NET Web API project.

Steps to reproduce

  1. Create two projects as follows
    • A .NET Standard 2.0 library
    • A classic ASP.NET Web API project targeting .NET Framework 4.6.1
  2. Define an interface and an implementing class pair in the .NET standard library as follows:
public interface IFoo { void DoSomething(); }

public class Foo : IFoo
{
    private readonly HttpClient _httpClient;

    public Foo(HttpClient httpClient) => _httpClient = httpClient;

    public void DoSomething() { /* No-op */ }
}
  1. Reference the .NET Standard library from the ASP.NET Web API project
  2. Install Autofac and Autofac.WebApi2 NuGet packages in the ASP.NET Web API project
  3. Implement a dummy controller that takes IFoo as a constructor parameter, like:
public class DummyController : ApiController
{
    private readonly IFoo _foo;

    public DummyController(IFoo foo) => _foo = foo;

    public IHttpActionResult Test() => Ok("a");
}
  1. Implement the relevant container registration as follows:
public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        GlobalConfiguration.Configure(WebApiConfig.Register);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        var builder = new ContainerBuilder();
        var config = GlobalConfiguration.Configuration;

        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
        builder.RegisterWebApiFilterProvider(config);

        // Problematic binding here
        builder.Register(c => new Foo(new HttpClient())).As<IFoo>().SingleInstance();

        var container = builder.Build();
        config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
    }
}
  1. Run the ASP.NET Web API application and visit /api/dummy/test.

Expected behaviour

"a" is returned.

Actual behaviour

Autofac.Core.DependencyResolutionException is thrown.

Stack trace:

An error has occurred.An error occurred when trying to create a controller of type 'DummyController'. Make sure that the controller has a parameterless public constructor.System.InvalidOperationException   at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
   at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)
   at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()An error has occurred.An error occurred during the activation of a particular registration. See the inner exception for details. Registration: Activator = DummyController (ReflectionActivator), Services = [AutofacNetStandard.Web.Controllers.DummyController], Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime, Sharing = None, Ownership = OwnedByLifetimeScope ---> An error occurred during the activation of a particular registration. See the inner exception for details. Registration: Activator = Foo (DelegateActivator), Services = [AutofacNetStandara.Library.IFoo], Lifetime = Autofac.Core.Lifetime.RootScopeLifetime, Sharing = Shared, Ownership = OwnedByLifetimeScope ---> Method not found: 'Void AutofacNetStandara.Library.Foo..ctor(System.Net.Http.HttpClient)'. (See inner exception for details.) (See inner exception for details.)Autofac.Core.DependencyResolutionException   at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)
   at Autofac.Core.Resolving.InstanceLookup.Execute()
   at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance)
   at Autofac.ResolutionExtensions.ResolveOptionalService(IComponentContext context, Service service, IEnumerable`1 parameters)
   at Autofac.ResolutionExtensions.ResolveOptional(IComponentContext context, Type serviceType)
   at Autofac.Integration.WebApi.AutofacWebApiDependencyScope.GetService(Type serviceType)
   at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator)
   at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)An error has occurred.An error occurred during the activation of a particular registration. See the inner exception for details. Registration: Activator = Foo (DelegateActivator), Services = [AutofacNetStandara.Library.IFoo], Lifetime = Autofac.Core.Lifetime.RootScopeLifetime, Sharing = Shared, Ownership = OwnedByLifetimeScope ---> Method not found: 'Void AutofacNetStandara.Library.Foo..ctor(System.Net.Http.HttpClient)'. (See inner exception for details.)Autofac.Core.DependencyResolutionException   at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)
   at Autofac.Core.Resolving.InstanceLookup.b__5_0()
   at Autofac.Core.Lifetime.LifetimeScope.GetOrCreateAndShare(Guid id, Func`1 creator)
   at Autofac.Core.Resolving.InstanceLookup.Execute()
   at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.InstanceLookup.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.Core.Activators.Reflection.AutowiringParameter.<>c__DisplayClass0_0.b__0()
   at Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate()
   at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)An error has occurred.Method not found: 'Void AutofacNetStandara.Library.Foo..ctor(System.Net.Http.HttpClient)'.System.MissingMethodException   at AutofacNetStandard.Web.WebApiApplication.<>c.b__0_0(IComponentContext c)
   at Autofac.RegistrationExtensions.<>c__DisplayClass5_0`1.b__0(IComponentContext c, IEnumerable`1 p)
   at Autofac.Builder.RegistrationBuilder.<>c__DisplayClass0_0`1.b__0(IComponentContext c, IEnumerable`1 p)
   at Autofac.Core.Activators.Delegate.DelegateActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)

Autofac filter interface example not available

The documentation (http://docs.autofac.org/en/latest/integration/webapi.html) still shows the old style and is not updated. Would appreciate if someone could help me with changing the below code to new style.

public void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            if (actionExecutedContext.Response != null)
                actionExecutedContext.Response.Headers.Add("P3P", "CP=\\\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\\\"");

        }

        public void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
        {

        }

registering the same..

builder.Register(c => new myActionFilter())
            .AsWebApiActionFilterFor<DefaultController>(c => c.myaction(default(myclass)))
            .InstancePerRequest();

Thanks

HttpRequestMessage is disposed after the filters are resolved

Describe the Bug

HttpRequestMessage is disposed after the filters are resolved in some situation

Steps to Reproduce

My code: AutofacRequestMessageDisposed.zip

public class ReproTest
{
        [Fact]
        public async Task RegisterHttpRequestMessageNotDisposeAfterScopeAsync()
        {
            var config = new HttpConfiguration();
            var builder = new ContainerBuilder();

            config.RegisterHttpRequestMessage(builder);

            var container = builder.Build();
            Assert.True(container.IsRegistered<HttpRequestMessage>());

            var httpRequestMessage = new HttpRequestMessage
            {
                Content = new StringContent("")
            };

            HttpRequestMessageProvider.Current = httpRequestMessage;
            var result = HttpRequestMessageProvider.Current;


            using(var scope = container.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag))
            {
                Assert.Same(result, scope.Resolve<HttpRequestMessage>());
            }

            _ = await result.Content.ReadAsStringAsync();
        }
}

Expected Behavior

Autofac.WebAPI not dispose it

Exception with Stack Trace

<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Cannot access a disposed object. Object name: 'System.Web.Http.WebHost.HttpControllerHandler+LazyStreamContent'.
</ExceptionMessage>
<ExceptionType>System.ObjectDisposedException</ExceptionType>
<StackTrace>
at System.Net.Http.HttpContent.CheckDisposed() at System.Net.Http.HttpContent.ReadAsStringAsync() at AutofacRequestMessageDisposed.Filters.SomeDependency.<DoSomething>d__2.MoveNext() in C:\Users\RAD17\source\repos\AutofacRequestMessageDisposed\AutofacRequestMessageDisposed\Filters\CheckRequestFilter.cs:line 33 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at AutofacRequestMessageDisposed.Filters.CheckRequestFilter.<ExecuteAuthorizationFilterAsync>d__2.MoveNext() in C:\Users\RAD17\source\repos\AutofacRequestMessageDisposed\AutofacRequestMessageDisposed\Filters\CheckRequestFilter.cs:line 18 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__15.MoveNext()
</StackTrace>
</Error>

Dependency Versions

Autofac: 6.0.0/5.2.0
Autofac.WebAPI: 6.0.0/5.0.0

Injecting a registered named service is not working as expected.

Background :

  • Autofac 4.2.1(tried with the latest version of Autofac and the issue is still present) ,
  • WebApi project .netcore 2.2

Issue :
I have two instances of the same service, which I register to the ContainerBuilder with Name:

        builder.RegisterType<ServiceOne>().Named<IService>("ServiceOne").SingleInstance();
        builder.RegisterType<ServiceTwo>().Named<IService>("ServiceTwo").SingleInstance();

I then inject the named serivces to my controller classes as required:

        builder.Register(c => new KeysController(c.ResolveNamed<IService>("ServiceOne")));
        builder.Register(c => new ValuesController(c.ResolveNamed<IService>("ServiceTwo")));

Autofac is not able to resolve the IService for both of my controllers by name.

The Exception :
An unhandled exception occurred while processing the request.
InvalidOperationException: Unable to resolve service for type 'AutoFacNaming.IService' while attempting to activate 'AutoFacNaming.Controllers.ValuesController'.

I have attached a sample project which outlines the issue.
autofacNaming.zip

ActionFilter should not be called if HttpResponseMessage had been populated by previous ActionFilter

Let's imagine we have a controller:

namespace WebApplication.Controllers
{
    using System.Collections.Generic;
    using System.Web.Http;

    public class ValuesController : ApiController
    {
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }
    }
}

Now we want to decorate it with two action filters

Filter1

public class Filter1 : IActionFilter
{
    public bool AllowMultiple { get; }

    public async Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
    {
        Debug.WriteLine("Filter 1 invoked");
        return actionContext.Request.CreateErrorResponse(HttpStatusCode.Forbidden, "forbidden");
    }
}

Filter2

public class Filter2 : IActionFilter
{
    public bool AllowMultiple { get; }

    public async Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
    {
        Debug.WriteLine("Filter 2 invoked");
        ... // do something
    }
}

Filters added to HttpConfiguration.Filters:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services

        config.Filters.Add(new Filter1());
        config.Filters.Add(new Filter2());
    }
}

Now if we run an app and navigate to route, in Output Window will be shown only "Filter 1 invoked", Filter2 will not be fired.

################################################################################

If we make the same only using IAutofacActionFilter, both filters will be fired

Filter1

public class Filter1 : IAutofacActionFilter
{
    public async Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
    {
    }

    public async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
    {
        Debug.WriteLine("Filter 1 invoked");
        actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Forbidden, "forbidden");

    }
}

Filter2

public class Filter2 : IAutofacActionFilter
{
    public async Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
    {
    }

    public async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
    {
        Debug.WriteLine("Filter 2 invoked");
        // do something
    }
}

Global.asax.cs

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        GlobalConfiguration.Configure(WebApiConfig.Register);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        var builder = new ContainerBuilder();

        var config = GlobalConfiguration.Configuration;

        Assembly assembly = Assembly.GetExecutingAssembly();
        builder.RegisterApiControllers(assembly).PropertiesAutowired();
        builder.RegisterWebApiFilterProvider(config);

        builder.Register(c => new Filter1()).AsWebApiActionFilterFor<ValuesController>().InstancePerRequest();
        builder.Register(c => new Filter2()).AsWebApiActionFilterFor<ValuesController>().InstancePerRequest();
        var container = builder.Build();
        config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
    }
}

Run an app and navigate to route, in Output Window will be shown "Filter 1 invoked", "Filter 2 invoked". Property Response of actionContext parameter in OnActionExecutingAsync method of Filter2 equal to Forbidden response.

Libraries:

Autofac 4.6.2
Autofac.WebApi2 4.1.0

Dependency Issue on latest version on nuget

There is some type of dependency issue with the latest version of Autofac.WebApi2 (https://www.nuget.org/packages/Autofac.WebApi2/) on nuget. According to nuget these are the dependencies:

Autofac (>= 3.5.0 && < 5.0.0)
Microsoft.AspNet.WebApi.Core (>= 5.2.0 && < 6.0.0)
Newtonsoft.Json (>= 5.0.8)

I already had Autofac 4.6.2 installed before i installed the package. Here is the error i get:
image

System.IO.FileLoadException: 'Could not load file or assembly 'Autofac, Version=3.5.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)'

Playing around with versions of autofac it appears that 3.5.0 or 3.5.2 will work but as soon as you upgrade to >= 4.0.0 then you will get the error but according to the dependencies above it should work. I'm using .Net framework 4.6

Edit: Now i'm kind of stuck because Autofac.Mvc5 requires >=4.0.1 && < 5.0.0.

Override Exception Filters executed last instead of first

Describe the Bug

When using exception filters, the ones registered with override are executed last not first, which is the opposite compared to action filters. The documentation sounds like it should be the same order no matter what type of interface you implement so it would seem like a bug.

Steps to Reproduce

[RoutePrefix("api/healthcheck")]
public class HealthCheckController : ApiController
    {
        [HttpGet]
        [Route]
        public IEnumerable<string> Get()
        {
            throw new InvalidDataException("TEST");
        }
    }

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

            var builder = new ContainerBuilder();

            builder.RegisterType<HealthCheckController>()
                .InstancePerRequest();

            builder.RegisterType<E1>()
                .AsWebApiExceptionFilterOverrideFor<HealthCheckController>()
                .SingleInstance();

            builder.RegisterType<E2>()
                .AsWebApiExceptionFilterOverrideForAllControllers()
                .SingleInstance();

            builder.RegisterType<E3>()
                .AsWebApiExceptionFilterFor<HealthCheckController>()
                .SingleInstance();

            builder.RegisterType<E4>()
                .AsWebApiExceptionFilterForAllControllers()
                .SingleInstance();

            builder.RegisterWebApiFilterProvider(config);

            builder.RegisterWebApiModelBinderProvider();

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

    public class E1 : IAutofacExceptionFilter
    {
        public Task OnExceptionAsync(
            HttpActionExecutedContext actionExecutedContext,
            CancellationToken cancellationToken)
        {
            Trace.WriteLine("E1-exception");
            return Task.CompletedTask;
        }
    }

    public class E2 : IAutofacExceptionFilter
    {
        public Task OnExceptionAsync(
            HttpActionExecutedContext actionExecutedContext,
            CancellationToken cancellationToken)
        {
            Trace.WriteLine("E2-exception");
            return Task.CompletedTask;
        }
    }

    public class E3 : IAutofacExceptionFilter
    {
        public Task OnExceptionAsync(
            HttpActionExecutedContext actionExecutedContext,
            CancellationToken cancellationToken)
        {
            Trace.WriteLine("E3-exception");
            return Task.CompletedTask;
        }
    }

    public class E4 : IAutofacExceptionFilter
    {
        public Task OnExceptionAsync(
            HttpActionExecutedContext actionExecutedContext,
            CancellationToken cancellationToken)
        {
            Trace.WriteLine("E4-exception");
            return Task.CompletedTask;
        }
    }

Expected Behavior

When I do something very similar above, but with IAutofacContinuationActionFilter instead of IAutofacExceptionFilter (and changing registration to use AsWebApiActionFilter... I get the following printed in order

E1 - continue
E2 - continue
E3 - continue
E4 - continue

But when I run the code above I get this printed in order:

E3-exception
E4-exception
E1-exception
E2-exception

which means the override ones are executed last, not first.

Dependency Versions

Autofac.WebApi2 6.0.1
Autofac 6.2.0

Bad async/await implementation causing loss of execution context.

Very commonly, filters are used to provide execution context around an action. For example, wrapping an action method with a TransactionScope, LogContext, AsyncLocal variables, etc. When operating in an asynchronous environment using async/await, many of these things flow through the action through the use of the ExecutionContext.

The current implementation of ActionFilterWrapper simply iterates over a collection of filters and naively calls await for each filter in turn. The problem with this is that whenever await is used any state set on the execution context of the underlying filter will be lost since await will restore the current execution context upon the completion of the underlying Task. This makes it impossible to do the kind of things mentioned above, such as using TransactionScope with the TransactionScopeAsyncFlowOption.Enabled, or setting any AsyncLocal variables.

In order to work property, the execution of asynchronous filters needs to be chained. An example of this can be seen in how plain AttributeFilters are executed by the framework:

 Func<Task<HttpResponseMessage>> result = innerAction;
            for (int i = filters.Length - 1; i >= 0; i--)
            {
                IActionFilter filter = filters[i];
                Func<Func<Task<HttpResponseMessage>>, IActionFilter, Func<Task<HttpResponseMessage>>>
                    chainContinuation = (continuation, innerFilter) =>
                    {
                        return () => innerFilter.ExecuteActionFilterAsync(actionContext, cancellationToken,
                            continuation);
                    };
                result = chainContinuation(result, filter);
            }
            return result;

I would like to submit a pull request, but wanted to check before I do if this makes sense to everyone. This has a small potential under rare circumstances to be a breaking change, but as it stands I can't migrate from 3.x to 4.x because almost all of the filters we use would require proper async support without losing execution context.

Registration of filters on base classes no longer work

I have two filters set on two different base classes. I then have a controller (PersController) that derives from both of them.

containerBuilder.RegisterType<ExceptionFilter>()
	.As<IAutofacExceptionFilter>()
	.AsWebApiExceptionFilterFor<ApiController>()
	.InstancePerRequest();

containerBuilder.RegisterType<AuthenticationFilter>()
	.As<IAutofacAuthenticationFilter>()
	.AsWebApiAuthenticationFilterFor<AuthenticationControllerBase>()
	.InstancePerRequest();
	
public class PersController : AuthenticationControllerBase
{
}

public abstract class AuthenticationControllerBase : ApiController
{
}

With latest version (v4.3.0), if I call an API in PersController, I don't see either of these filters run. In version v4.2.1, this worked.

If I change the registration to the following, then it works:

containerBuilder.RegisterType<ExceptionFilter>()
	.As<IAutofacExceptionFilter>()
	.AsWebApiExceptionFilterFor<PersController>()
	.InstancePerRequest();

containerBuilder.RegisterType<AuthenticationFilter>()
	.As<IAutofacAuthenticationFilter>()
	.AsWebApiAuthenticationFilterFor<PersController>()
	.InstancePerRequest();

This is a huge change and I assume that this is a bug, not that this was an intended change.

Registering one filter type for multiple controllers doesn't work in one statement

When registering filters, you can't register one type for multiple controllers like this:

builder.RegisterType<CustomActionFilter>()
            .AsWebApiActionFilterFor<ProductsController>()
            .AsWebApiActionFilterFor<OrdersController>()
            .InstancePerRequest();

Rather, you'll have to do it per controller separately:

builder.RegisterType<CustomActionFilter>()
            .AsWebApiActionFilterFor<ProductsController>()
            .InstancePerRequest();

builder.RegisterType<CustomActionFilter>()
            .AsWebApiActionFilterFor<OrdersController>()
            .InstancePerRequest();

This can become cumbersome, because often, you'd want to use certain filters for lots, if not all, controllers.

You can't use the first option, because you get an exception stating that an item with the same key has already been added to the dictionary. The key is AutofacWebApiFilterProvider.ActionFilterMetadataKey and has a value of "AutofacWebApiActionFilter".

The problem is that in the AsFilterFor method, metadata is constructed that will be unique in the registration (because of a different controller type), but won't be added with a unique metadatakey (always "AutofacWebApiActionFilter").

I'd be happy to fix this and submit a PR, if you're willing to guide me where and how (and if) you'd like it fixed. Can I safely change the metadatakey to be something unique?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.