Saturday, May 16, 2015

Setting up Web API 2 with Windsor using OWIN hosted in IIS


tldr; Get the code from my Github repository here: https://github.com/devdaves/ExampleWebApi

Start a new ASP.NET Web Application

image

Select the Empty template.  Don't check any of the core references (Web Forms, MVC or Web API).

image

Once the project is ready to go, open the package manager console and run the following commands to install the necessary nuget packages.

  • install-package Microsoft.AspNet.WebApi.Owin
  • install-package Microsoft.Owin.Host.SystemWeb
  • install-package Castle.Windsor

Open the web.config and add the following code:

<appSettings>
  <add key="owin:AutomaticAppStartup" value="true" />
</appSettings>

In the Solution Explorer create a Startup class at the root of the site with the following code:

public class Startup
{
    public void Configuration(IAppBuilder appBuilder)
    {
        HttpConfiguration config = new HttpConfiguration();
        config.MapHttpAttributeRoutes();
        appBuilder.UseWebApi(config);
    }
}

In the Solution Explorer create a Models folder and inside the Models folder create a Contact class with the following code:

public class Contact
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

In the Solution Explorer create a Controllers folder and inside the Controllers folder create a Web API controller called ContactsController with the following code:

public class ContactsController : ApiController
{
    [HttpGet]
    [Route("contacts")]
    public IHttpActionResult Get()
    {
        return Ok(new Contact(){FirstName = "Donald", LastName = "Duck"});
    }
}

At this point you have enough to run the application and should be able to go to http://localhost:####/contacts in your browser and see the contact returned.

Now its time to add Windsor.  In the Solution Explorer add a new folder called Windsor.  Create a new class called WindsorDependencyScope with the following code:

internal class WindsorDependencyScope : IDependencyScope
{
    private readonly IWindsorContainer _container;
    private readonly IDisposable _scope;

    public WindsorDependencyScope(IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }

        _container = container;
        _scope = container.BeginScope();
    }

    public object GetService(Type t)
    {
        return _container.Kernel.HasComponent(t)
        ? _container.Resolve(t) : null;
    }

    public IEnumerable<object> GetServices(Type t)
    {
        return _container.ResolveAll(t)
        .Cast<object>().ToArray();
    }

    public void Dispose()
    {
        _scope.Dispose();
    }
}

Add a new class to the Windsor folder called WindsorHttpDependencyResolver with the following code:

internal sealed class WindsorHttpDependencyResolver : IDependencyResolver
{
    private readonly IWindsorContainer _container;

    public WindsorHttpDependencyResolver(IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }

        _container = container;
    }

    public object GetService(Type t)
    {
        return _container.Kernel.HasComponent(t)
            ? _container.Resolve(t) : null;
    }

    public IEnumerable<object> GetServices(Type t)
    {
        return _container.ResolveAll(t)
        .Cast<object>().ToArray();
    }

    public IDependencyScope BeginScope()
    {
        return new WindsorDependencyScope(_container);
    }

    public void Dispose()
    {
    }
}

In the Windsor folder create a new folder called Installers.  Create a new class in the Installers folder called ControllerInstaller with the following code:

public class ControllerInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly()
        .Pick().If(t => t.Name.EndsWith("Controller"))
        .Configure(configurer => configurer.Named(configurer.Implementation.Name))
        .LifestylePerWebRequest());
    }
}

Create another new class in the Windsor/Installers folder called DefaultInstaller with the following code:

public class DefaultInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly()
            .Pick()
            .WithServiceDefaultInterfaces()
            .Configure(c => c.LifestyleTransient()));
    }
}

Now we need to make the final changes to the Startup class we created in the root of the site.  Make it look like this:

public class Startup
{
    public void Configuration(IAppBuilder appBuilder)
    {
        var container = new WindsorContainer().Install(
            new ControllerInstaller(),
            new DefaultInstaller());
        var httpDependencyResolver = new WindsorHttpDependencyResolver(container);

        HttpConfiguration config = new HttpConfiguration();
        config.MapHttpAttributeRoutes();
        config.DependencyResolver = httpDependencyResolver;
        appBuilder.UseWebApi(config);
    }
}

Now that Windsor is setup lets add a contact repository and add that as a dependency to the controller so we can see Windsor in action.

In the Solution Explorer add a folder called Repository.  In the Repository folder add a new interface called IContactsRepository with the following code:

public interface IContactsRepository
{
    Contact GetContact();
}

Add a class called ContactsRepository to the Repository folder with the following code:

public class ContactsRepository : IContactsRepository
{
    public Contact GetContact()
    {
        return new Contact(){FirstName = "Donald", LastName = "Duck"};
    }
}

Now lets go add the dependency to the ContactsController, make the ContactsController code look like this:

public class ContactsController : ApiController
{
    private IContactsRepository repository;

    public ContactsController(IContactsRepository repository)
    {
        this.repository = repository;
    }

    [HttpGet]
    [Route("contacts")]
    public IHttpActionResult Get()
    {
        return Ok(this.repository.GetContact());
    }
}

That should be it.  Running the site now everything should work.