22

I'm trying to resolve the dependencies of my custom AuthorizeAttribute which I use to decorate my API controllers in an MVC4 app. Problem is that I keep getting a NullReferenceException on the service dependency I use within my custom filter. Here is my Autofac configuration:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        var builder = new ContainerBuilder();
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
        builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerApiRequest();
        builder.RegisterType<DatabaseFactory>().As<IDatabaseFactory>().InstancePerApiRequest();
        builder.RegisterAssemblyTypes(typeof(UserProfileRepository).Assembly)
            .Where(t => t.Name.EndsWith("Repository"))
            .AsImplementedInterfaces().InstancePerApiRequest();

        builder.RegisterAssemblyTypes(typeof(IUserProfileMapper).Assembly)
            .Where(t => t.Name.EndsWith("Mapper"))
            .AsImplementedInterfaces().InstancePerApiRequest();

        builder.RegisterAssemblyTypes(typeof(UserProfileSvc).Assembly)
            .Where(t => t.Name.EndsWith("Svc"))
            .AsImplementedInterfaces().InstancePerApiRequest();

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

and my custom authorize filter:

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    public IAuthenticationSvc _authenticationSvc;
    protected override bool IsAuthorized(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        if (!base.IsAuthorized(actionContext))
        {
            return false;
        }
        var trueUserId = WebSecurity.CurrentUserId;

        if (_authenticationSvc.GetUsersRoles(trueUserId).Any(x => x == "Admin")) return true;
        // NullReferenceException on _authenticationSvc
     }
}

According to the official docs all that is needed is:

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

But that doesn't seem to do the trick either. Appreciate any help.

Rikard
  • 3,828
  • 1
  • 24
  • 39
Mohammad Sepahvand
  • 17,364
  • 22
  • 81
  • 122
  • 1
    I think _authenticationSvc needs to be a property not a field – MikeSW Apr 26 '14 at 14:42
  • 1
    @MikeSW Now it gives me this exception: `No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested. This generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario.) Under the web integration always request dependencies from the DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime, never from the container itself.` – Mohammad Sepahvand Apr 26 '14 at 15:18

4 Answers4

31

I think Autofac's documentation offers much simpler solution for WebApi action filters.

public interface ServiceCallActionFilterAttribute : ActionFilterAttribute
{
  public override void OnActionExecuting(HttpActionContext actionContext)
  {
    // Get the request lifetime scope so you can resolve services.
    var requestScope = actionContext.Request.GetDependencyScope();

    // Resolve the service you want to use.
    var service = requestScope.GetService(typeof(IMyService)) as IMyService;

    // Do the rest of the work in the filter.
    service.DoWork();
  }
}

It is not "pure DI" as it is using service locator, but it is simple and works with the request scope. You don't need to worry about registering specific action filter for each WebApi controller.

Source: http://autofac.readthedocs.io/en/latest/integration/webapi.html#provide-filters-via-dependency-injection

Martin Brabec
  • 3,720
  • 2
  • 23
  • 26
  • 2
    I'd have to say I prefer this over property injection. – Phil Cooper Apr 25 '18 at 19:13
  • 2
    It's nice an easy to understand, but if you intent to write tests, property injection is an easier solution. – Dina May 09 '18 at 23:59
  • @PhilCooper you'll end up having problems with such filter during unit testing. how are we supposed to mock it then? – Alex Herman Oct 24 '18 at 10:49
  • @AlexHerman you could provide a constructor your tests would use which would provide the mechanism `Func serviceFactory` for resolving types while your default (empty) constructor would simply use the example above. `actionContext => actionContext.Request.GetService(typeof(IMyService)) as IMyService`. – Phil Cooper Oct 24 '18 at 12:17
  • worked now, i forgot to register my service in AutoFac – Jitendra Pancholi Dec 28 '18 at 07:10
27

You should configure property injection for your attribute

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    public IAuthenticationSvc AuthenticationSvc { get; set; }
}

and the builder

builder.RegisterType<MyAuthorizeAttribute>().PropertiesAutowired();
Rikard
  • 3,828
  • 1
  • 24
  • 39
Toan Nguyen
  • 11,263
  • 5
  • 43
  • 59
  • 4
    I think this answer may work but it's not the correct one. Because 'AuthorizeAttribute' and totally filters in webApi have one instance and if you have simultaneous requests it may make some problems. In fact, it's not a thread-safe solution. Am I right? – MohammadReza Daghestani May 14 '18 at 06:51
  • @MohammadRezaDaghestani I believe you are right as that's the issue I am facing atm. – Kyle Huang Sep 25 '19 at 00:09
15

In addition to @Toan Nguyen's answer, if you have this...

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    public IAuthenticationSvc AuthenticationSvc { get; set; }
}

... it seems you also need (or may need) the first line below:

builder.RegisterFilterProvider();
builder.RegisterType<MyAuthorizeAttribute>().PropertiesAutowired();

Reference: http://itprojectpool.blogspot.com.au/2014/03/autofac-di-on-action-filters.html

jwwishart
  • 151
  • 1
  • 2
  • 1
    Yep, that first line fixed it for me. – Austin Jul 06 '16 at 18:26
  • 1
    Well, this is for MVC right? The example code in the question is for the WebAPI registration. Although the question is tagged for MVC, too, so this information is pertinent, it should just be clear that the RegisterFilterProvider method is the MVC equivalent to `RegisterWebApiFilterProvider` I think – Adam Plocher Feb 10 '18 at 09:16
1

In addition to configuring property injection, as outlined in other answers, you can also explicitly resolve dependencies in the OnActivating callback.

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    private IAuthenticationSvc _authenticationSvc;
    public void SetAuthenticationSvc(IAuthenticationSvc svc)
    {
       this._authenticationSvc = svc;
    }
}

And then register the type:

builder.RegisterType<MyAuthorizeAttribute>()
    .OnActivating(_ => _.Instance.SetAuthenticationSvc(_.Context.Resolve<IAuthenticationSvc>()));

Note: You can do the same with a property instead of a method. I chose to use a method here only to illustrate that this solution was not dependent on PropertiesAutowired.

Igor
  • 60,821
  • 10
  • 100
  • 175