How to use DI container when OwinStartup
Asked Answered
B

3

69

It's a Web API 2 project.

When I implement DI using Ninject, I got an error message

An error occurred when trying to create a controller of type 'TokenController'. Make sure that the controller has a parameterless public constructor.

[assembly: OwinStartup(typeof(Web.Startup))]

namespace Web
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);
            ConfigureWebApi(app);
        }
    }
}

public class TokenController : ApiController
{

    private IUserService _userService;

    public TokenController(IUserService userService)
    {
        this._userService = userService;
    }

    [Route("api/Token")]
    public HttpResponseMessage PostToken(UserViewModel model)
    {
        if (_userService.ValidateUser(model.Account, model.Password))
        {
            ClaimsIdentity identity = new ClaimsIdentity(Startup.OAuthBearerOptions.AuthenticationType);
            identity.AddClaim(new Claim(ClaimTypes.Name, model.Account));
            AuthenticationTicket ticket = new AuthenticationTicket(identity, new AuthenticationProperties());
            var currentUtc = new SystemClock().UtcNow;
            ticket.Properties.IssuedUtc = currentUtc;
            ticket.Properties.ExpiresUtc = currentUtc.Add(TimeSpan.FromMinutes(30));
            return new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new ObjectContent<object>(new
                {
                    UserName = model.Account,
                    AccessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket)
                }, Configuration.Formatters.JsonFormatter)
            };
        }

        return new HttpResponseMessage(HttpStatusCode.BadRequest);
    }
}

When I add <add key="owin:AutomaticAppStartup" value="false" /> to web.config

Ninject works fine, but Startup.OAuthBearerOptions.AccessTokenFormat becomes to null

How to use DI container with OWIN?

UPDATE

Implement IDependencyResolver and use the WebAPI Dependency Resolver as below

public void ConfigureWebApi(IAppBuilder app)
{
    HttpConfiguration config = new HttpConfiguration();

    config.DependencyResolver = new NinjectDependencyResolver(NinjectWebCommon.CreateKernel());

    app.UseWebApi(config);
}

NinjectDependencyResolver


In Simple Injector case

public void ConfigureWebApi(IAppBuilder app)
{
    HttpConfiguration config = new HttpConfiguration();

    var container = new Container();
    container.Register<IUserService, UserService>();
    config.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);

    app.UseWebApi(config);
}

SimpleInjectorWebApiDependencyResolver

Budworth answered 5/11, 2013 at 4:33 Comment(4)
Is there a reason why you can't add an empty constructor to your controller?Annecorinne
@NikolaiSamteladze I have already implement dependency inject in empty constructor, I just want to know how to use DI container in this caseBudworth
There is a project which provides this support, but maybe you'll have to write the adapter for ninject yourself as it provides an out of the box implementation for autofac only.Canopus
Try it and if it answers your question, come back and let me know, I'll make it an answer. Till then let's see if anyone can provide any better way.Canopus
S
35

You might want to take a look at this blog post.

It's using Unity but it should end-up being the same.

Basically, use the WebAPI Dependency Resolver. Make sure that everything is mapped properly and it should be fine.

If after setting up your DI you still have problem with your OAuth token, let me know.

Cheers

Scraggly answered 5/12, 2013 at 14:5 Comment(1)
I use the WebAPI Dependency Resolver and it works, thanks for your answer!Budworth
A
17

Update

This is now more straight forward thanks to the Nuget package Ninject.Web.WebApi.OwinHost:

Startup.cs

using Ninject;
using Ninject.Web.Common.OwinHost;
using Ninject.Web.WebApi.OwinHost;
using Owin;
using System.Web.Http;

namespace Xxx
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            var config = new HttpConfiguration();
            config.MapHttpAttributeRoutes();
            config.Routes.MapHttpRoute("DefaultApi", "myservice/{controller}/{id}", new { id = RouteParameter.Optional });

            app.UseNinjectMiddleware(CreateKernel);
            app.UseNinjectWebApi(config);
        }
    }
    public static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();

        kernel.Bind<IMyService>().To<MyService>();
        return kernel;
    }
}

I have updated the wiki accordingly.

https://github.com/ninject/Ninject.Web.Common/wiki/Setting-up-a-OWIN-WebApi-application

All three hosting options.

https://github.com/ninject/Ninject.Web.WebApi/wiki/Setting-up-an-mvc-webapi-application

Autoicous answered 29/7, 2014 at 11:57 Comment(1)
I have tried this for a very simple, example owin hosted application and it works only for the first call. Subsequent calls lead to the error: "Make sure that the controller has a parameterless public constructor.". Any ideas why?Sportive
R
2

We use the standard ninject.MVC5 package installed with nuget

PM> install-package ninject.MVC5

Then we configure our bindings like so.

kernel.Bind<IDbContext, DbContext>()
    .To<BlogContext>()
    .InRequestScope();

kernel.Bind<IUserStore<User>>()
    .To<UserStore<User>>()
    .InRequestScope();

kernel.Bind<IDataProtectionProvider>()
    .To<DpapiDataProtectionProvider>()
    .InRequestScope()
    .WithConstructorArgument("ApplicationName");

kernel.Bind<ApplicationUserManager>().ToSelf().InRequestScope()
    .WithPropertyValue("UserTokenProvider",
        new DataProtectorTokenProvider<User>(
            kernel.Get<IDataProtectionProvider>().Create("EmailConfirmation")
            ));

You may need to adjust dependent on how much you have customized your user model. For instance the user store binding may be something like.

kernel.Bind<IUserStore<User, int>>()
      .To<IntUserStore>().InRequestScope();

Also any setting up of the user manger you require i.e. password policies can be set in your user manager constructor.

Previously this could be found in the create method in the sample, you will no longer require this. also you can now get rid of the owin get context calls as ninject will handle resolution for you.

Retrenchment answered 8/7, 2014 at 20:34 Comment(2)
There has been chatter that internally some of the Identity stuff has dependencies on OWIN (i.e. it makes calls to OwinContext.Get()). Have you seen any oddness or problems related to this?Brougham
@Brougham No can't say we've hit any issues, been using this setup on projects in production for some time now.Retrenchment

© 2022 - 2024 — McMap. All rights reserved.