shashi
shashi

Reputation: 4696

How to implement DI for an attribute in WebApi?

I refactored an attribute, which implements Basic Http Authentication in the Web api, to have DI as follows:

 public class BasicHttpAuthAttribute : ActionFilterAttribute
    {
        private readonly ILoginManager _manager;

        public BasicHttpAuthAttribute(ILoginManager manager)
        {
            this._manager = manager;
        }
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if (actionContext.Request.Headers.Authorization == null)
            {
                actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
                actionContext.Response.Content = new StringContent("Missing Auth-Token");
            }
            else
            {

                var authToken = actionContext.Request.Headers.Authorization.Parameter;
                var decodedToken = Encoding.UTF8.GetString(Convert.FromBase64String(authToken));

                string userName = decodedToken.Substring(0, decodedToken.IndexOf(":"));
                string password = decodedToken.Substring(decodedToken.IndexOf(":") + 1);


                UserInfo user;


                if (_manager.LoginPasswordMatch(userName, password, out user))
                {
                    var apiUser = new ApiUser(user.UserID);
                    HttpContext.Current.User = new GenericPrincipal(new ApiIdentity(apiUser), new string[]{});

                    base.OnActionExecuting(actionContext);

                }
                else
                {
                    actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
                    actionContext.Response.Content = new StringContent("Invalid username or password");
                }



            }
        }
    }

Before the refactoring, I was creating an instance of the LoginManager (which itself did not have DI so I could create an instance using a ctor) inside OnActionExecuting. The problem after refactoring is that the build fails because when I apply the filter to a WebApi method, it is expecting an argument there.

How do I implement DI in this case as the LoginManager itself takes a ILoginRepository in its constructor? Is it even possible?

Upvotes: 1

Views: 296

Answers (3)

Sebastian Weber
Sebastian Weber

Reputation: 6806

You don't control the instantiation of the BasicHttpAuthAttribute so you can't use proper DI.

You can use a ServiceLocator in your attribute's constructor to give you the required dependencies or you can use the BuildUp functionality some containers provide for existing objects.

Unity does this to support BuildUp for 3rd party objects that you don't create yourself.

For your attribute that would mean to create a public writable property of type ILoginManager and tell the container to inject your manager into that property.

You don't have to pollute your attribute with the usage of the DependencyAttribute btw. Register your attribute type with the container and provide an InjectionProperty("NameOfThePropertyToInject") as a parameter for that registration.

Upvotes: 1

Filip W
Filip W

Reputation: 27187

It's not easy to do because the way attributes are treated in Web API is a bit different than what you might expect. First of all they are created at various point in time, and secondly they are cached.

With that said, rather than injection through a constructor, the easiest way is to achieve DI is to call the DI framework of your choice at processing time, and retrieving the dependency then, i.e. inside your onActionExecuting:

var s = GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IService));

Upvotes: 2

Tim Copenhaver
Tim Copenhaver

Reputation: 3302

The easiest option is to make it so your constructor does not require the parameter, but use a different form of injection instead. Exactly how you do that depends on what DI container you're using. With Unity, for example, you can just create a default constructor. Create another public method (I usually call it Initialize to be clear) and decorate it with the [InjectionMethod] attribute. Then, inside your constructor, just call container.BuildUp(this); That basically allows MVC to call the default constructor like it wants, but your InjectionMethod is always automatically called as a part of the construction process.

There are similar ways to do the same thing with other DI containers, but without knowing what you're using the Unit example is the easiest to explain.

Upvotes: 1

Related Questions