56

I'm writing a custom Authorization Filter for asp.net mvc 3. I need to inject a userservice into the class but I have no idea how to do this.

public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
    private IUserService userService;
    private string[] roles;

    public AuthorizeAttribute(params string[] roles)
    {
        this.roles = roles;
    }

    public void OnAuthorization(AuthorizationContext filterContext)
    {
        throw new NotImplementedException();
    }
}

I'm using ninject for dependency injection. I do not want to use a Factory or service locator pattern.

My bindings look like this in the global.acsx:

    internal class SiteModule : NinjectModule
    {
        public override void Load()
        {
            Bind<IUserService>().To<UserService>();
        }
    }
2
  • Are you sure you don't want to use factory? In that case you have to keep a reference to kernel in your Application class, and manually Get<IUserService>() in controller constructor. In Mvc 3 factory really makes sense as you are overriding the original one. There are ninject nugets for this also. Commented May 31, 2011 at 20:58
  • Jakubmal, could you make an answer using factories then? Commented May 31, 2011 at 21:16

4 Answers 4

82

See this answer: Custom Authorization MVC 3 and Ninject IoC

If you want to use constructor injection then you need to create an attribute and a filter.

/// Marker attribute
public class MyAuthorizeAttribute : FilterAttribute { }

/// Filter
public class MyAuthorizeFilter : IAuthorizationFilter
{
      private readonly IUserService _userService;
      public MyAuthorizeFilter(IUserService userService)
      {
          _userService = userService;
      }

      public void OnAuthorization(AuthorizationContext filterContext)
      {
          var validUser = _userService.CheckIsValid();

          if (!validUser)
          {
              filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary { { "action", "AccessDenied" }, { "controller", "Error" } });
          }
      }
}

Binding:

this.BindFilter<MyAuthorizeFilter>(System.Web.Mvc.FilterScope.Controller, 0).WhenControllerHas<MyAuthorizeAttribute>();

Controller:

[MyAuthorizeAttribute]
public class YourController : Controller
{
    // ...
}
Sign up to request clarification or add additional context in comments.

11 Comments

How would you setup the implementation to accomodate: [MyAuthorizeAttribute("Admin", "Contributer")]? Passing parameters.
github.com/ninject/ninject.web.mvc/wiki/Filter-configurations +1 For giving the answer that supports Constructor instead of Property Injection
@Lol coder - see Remo's link above for how to configure filters.
I still cant figure out how to setup the attribute constructor to accept both params and the service.
|
11

I would highly recommend B Z's answer. DO NOT use [Inject]!

I used an [Inject] like Darin Dimitrov said was possible and it actually caused threading issues under high load, high contention situations in conjunction with .InRequestScope.

B Z's way is also what is on the Wiki and I have seen many places where Remo Gloor (Ninject author) says this is the correct way to do it, e.g. https://github.com/ninject/ninject.web.mvc/wiki/Filter-configurations.

Downvote [Inject] answers in here because seriously you will get burned (probably in production if you don't load test properly beforehand!).

1 Comment

I did and no one would see it because its hidden by default (too far down). Based on the fact I spent 3 days trying to figure out what went wrong with Darin's answer I figure it is VERY relevant and worthy of an official answer to avoid anyone using it by mistake and getting burned
9

I found a simple solution for any occasion where construction is not handled by Ninject:

var session = (IMyUserService)DependencyResolver.Current.GetService(typeof (IMyUserService));

Actually this is exactly what I am using with my custom AuthorizeAttribute. Much easier than having to implement a separate FilterAttribute.

Comments

8

On way would be to use a property injection and decorate the property with the [Inject] attribute:

public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
    [Inject]
    public IUserService UserService { get; set; }

    private string[] roles;
  
    ...
}

Constructor injection doesn't work well with attributes as you will no longer be able to decorate controllers/actions with them. You could only use constructor injection with the filter binding syntax in Ninject:

public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
    private readonly IUserService userService;

    private string[] roles;

    public AuthorizeAttribute(IUserService userService, params string[] roles)
    {
        this.userService = userService;
        this.roles = roles;
    }
  
    ...
}

and then:

internal class SiteModule : Ninject.Modules.NinjectModule
{
    public override void Load()
    {
        Bind<IUserService>().To<UserService>();

        this.BindFilter<AuthorizeAttribute>(FilterScope.Controller, 0)
            .WhenControllerType<AdminController>();
    }
}

The BindFilter<> extension method is defined in the Ninject.Web.Mvc.FilterBindingSyntax namespace so make sure you have brought that into scope before calling it on a kernel.

8 Comments

What if I do not use constructor bindings and used the [Inject] attribute, will I be able to decorate the controllers/actions?
Any idea why UserService would still be null? I had this same problem with Providers too.
@Lol coder, no idea. Works fine for me. I've created a new ASP.NET MVC 3 application, installed the NInject.MVC3 NuGet package, declared a IUserService inteface and UserService implementation, declared a custom MyAuthorizeAttribute using property injection and used the generated App_Start/NinjectMVC3.cs to configure the kernel. Then I simply decorated my HomeController with the attribute and the injection worked fine.
Thank you for specifying the namespace!!! It's annoying to have to find the namespace that you need for extension methods!
I would highly recommend B Z's answer. DO NOT use [Inject] it can create race conditions under high load!
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.