We need to replace Web Api’s default controller activator with a custom implementation that uses the Ninject kernel.

This happens in the Ninject bootstrapper code:

private static IKernel CreateKernel()
{
    var kernel = new StandardKernel();
    try
    {
        kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
        kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

        RegisterServices(kernel);

        // Here's where we wire things up for Web Api
        GlobalConfiguration.Configuration.Services.Replace(
            typeof(IHttpControllerActivator), 
            new NinjectControllerActivator(kernel));

        return kernel;
    }
    catch
    {
        kernel.Dispose();
        throw;
    }
}

The custom controller activator:

using System;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using Ninject;

public class NinjectControllerActivator : IHttpControllerActivator
{
    private readonly IKernel kernel;

    public NinjectControllerActivator(IKernel kernel)
    {
        if (kernel == null) throw new ArgumentNullException("kernel");
        this.kernel = kernel;
    }

    public IHttpController Create(
        HttpRequestMessage request, 
        HttpControllerDescriptor controllerDescriptor, 
        Type controllerType)
    {
        var controller = (IHttpController)this.kernel.Get(controllerType);

        request.RegisterForDispose(new Release(() => this.kernel.Release(controller)));

        return controller;
    }

    private class Release : IDisposable
    {
        private readonly Action release;

        public Release(Action release)
        {
            this.release = release;
        }

        public void Dispose()
        {
            this.release();
        }
    }
}