.NET Web API in MVC 5 Areas with Api Help and Dependency Injection

I was trying to find out the best possible way to  create .NET Web API with the API Controller specific to ASPNET MVC 5 areas. I went through various blogs and articles and finally found one of the blog written by  Andrew Malkov  and it really helped me a lot. My next step is to use Dependency Injection using Unity and also provide a Help page for the API. The Help Link for creating the Help page was really useful for me.

I am sharing with you how I went up on implementing the same. Following are the activities that I have to work on

  • Create an MVC + API project in Visual Studio 2013
  • Create a PCL (Portable Class Library) project in solution
  • Create a model, interface and implementation in Portable Class Library
  • Add Areas in MVC project implementing Web API Controller
  • Implement Dependency Injection using Unity for Web API Controller
  • Implement Help Library for the API in MVC View

Create an MVC + API project in Visual Studio 2013

WebApi

Create an Portable Class Library (PCL) project in Visual Studio 2013 for Business Layer

Once the Application is created lets go ahead to add Portable Class Library (PCL) project to the same solution. Portable Class Library are used for importing libraries compatible to multiple device. This library will have the business logic with a model, an Interface and an implementation.

Following is my model object for Product

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Category { get; set; }
    public decimal Price { get; set; }
}

Creating an Interface for Product Business Layer

public interface IProductService
{
    IEnumerable<Product> GetAllProducts();
    Product GetProduct(int id);
}

Implementation of the Interface in PCL Business library

public class ProductService : IProductService
{
    public IEnumerable<Product> GetAllProducts()
    {
        Product[] products = new Product[]
        {
            new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 },
            new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M },
            new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M }
        };
        return products;
    }

    public Product GetProduct(int id)
    {
        Product[] products = new Product[]
        {
            new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 },
            new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M },
            new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M }
        };
        var product = products.FirstOrDefault((p) => p.Id == id);
        return product;
    }

That’s it we are done with the PCL implementation. We are going to add the reference of this PCL in our MVC project

Implement Dependency Injection using Unity in MVC project

In order to accomplish this aspect, install the package Unity through NuGet Package Manager Console for MVC Web Application

PM> install-package unity
Installing 'Unity 3.5.1404.0'.
You are downloading Unity from Microsoft, the license agreement to which is available at http://opensource.org/licenses/Apache-2.0. Check the package for additional dependencies, which may come with their own license agreement(s). Your use of the package and dependencies constitutes your acceptance of their license agreements. If you do not accept the license agreement(s), then delete the relevant components from your device.
Successfully installed 'Unity 3.5.1404.0'.
Adding 'Unity 3.5.1404.0' to WebApi.Demo1.
Successfully added 'Unity 3.5.1404.0' to WebApi.Demo1.

Next step is to a Unity Dependency Resolver in the project. Create a Resolver class and add the same in the project.

public class UnityResolver : IDependencyResolver
{
    protected IUnityContainer container;
    public UnityResolver(IUnityContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }
        this.container = container;
    }
    public IDependencyScope BeginScope()
    {
        var child = container.CreateChildContainer();
        return new UnityResolver(child);
    }

    public object GetService(Type serviceType)
    {
        try
        {
            return container.Resolve(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return null;
        }
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        try
        {
            return container.ResolveAll(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return new List<object>();
        }
    }

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

Now register the Unity container for WebApiConfig.cs file under App_Start folder. Add the types for the container. Here the type will be for Product Service created in Business Layer.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
        var container = new UnityContainer();
        container.RegisterType<IProductService, ProductService>(new HierarchicalLifetimeManager());
        config.DependencyResolver = new UnityResolver(container);
        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

Now next step is to create an Area named Product and implement an Web API Controller integrating the business logic layer service.

public class ProductsController : ApiController
{
    private readonly IProductService _service;
    public ProductsController(IProductService Service)
    {
        _service = Service;
    }
    public IEnumerable<Product> GetAllProducts()
    {
        return _service.GetAllProducts();
    }

    public IHttpActionResult GetProduct(int id)
    {
        var product = _service.GetProduct(id);
        if (product == null)
        {
            return NotFound();
        }
        return Ok(product);
    }


}

Once this is completed, our next step is to have the Web Api Controller recognized from MVC areas. For that we need to implement a AreaHttpControllerSelector that will be registered in WebAPI Area Config.

public class AreaHttpControllerSelector : System.Web.Http.Dispatcher.DefaultHttpControllerSelector
{
    private const string ControllerSuffix = "Controller";
    private const string AreaRouteVariableName = "area";

    private readonly System.Web.Http.HttpConfiguration _configuration;

    private Dictionary<string, Type> _apiControllerTypes;

    public AreaHttpControllerSelector(HttpConfiguration configuration)
        : base(configuration)
    {
        _configuration = configuration;
    }

    private Dictionary<string, Type> ApiControllerTypes
    {
        get { return _apiControllerTypes ?? (_apiControllerTypes = GetControllerTypes()); }
    }

    private static Dictionary<string, Type> GetControllerTypes()
    {
        var assemblies = AppDomain.CurrentDomain.GetAssemblies();

        var types = assemblies.SelectMany(a => a.GetTypes().Where(t => !t.IsAbstract && t.Name.EndsWith(ControllerSuffix) && typeof(IHttpController).IsAssignableFrom(t)))
            .ToDictionary(t => t.FullName, t => t);

        return types;
    }

    public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
    {
        return GetApiController(request) ?? base.SelectController(request);
    }

    private static string GetAreaName(HttpRequestMessage request)
    {
        var data = request.GetRouteData();

        if (!data.Values.ContainsKey(AreaRouteVariableName))
        {
            return null;
        }

        return data.Values[AreaRouteVariableName].ToString().ToLower();
    }

    private Type GetControllerTypeByArea(string areaName, string controllerName)
    {
        var areaNameToFind = string.Format(".{0}.", areaName.ToLower());
        var controllerNameToFind = string.Format(".{0}{1}", controllerName, ControllerSuffix);

        return ApiControllerTypes.Where(t => t.Key.ToLower().Contains(areaNameToFind) && t.Key.EndsWith(controllerNameToFind, StringComparison.OrdinalIgnoreCase))
                .Select(t => t.Value).FirstOrDefault();
    }

    private HttpControllerDescriptor GetApiController(HttpRequestMessage request)
    {
        var controllerName = base.GetControllerName(request);

        var areaName = GetAreaName(request);
        if (string.IsNullOrEmpty(areaName))
        {
            return null;
        }

        var type = GetControllerTypeByArea(areaName, controllerName);
        if (type == null)
        {
            return null;
        }

        return new HttpControllerDescriptor(_configuration, controllerName, type);
    }


}

Update the RegisterArea method with the following code and comment the previous code block

public class TestAreaRegistration : AreaRegistration
{
    public override string AreaName
    {
        get
        {
            return "Test";
        }
    }

    public override void RegisterArea(AreaRegistrationContext context)
    {
        //context.MapRoute(
        //    "Test_default",
        //    "Test/{controller}/{action}/{id}",
        //    new { action = "Index", id = UrlParameter.Optional }
        //);
        context.Routes.MapHttpRoute(
            name: "Admin_Api",
            routeTemplate: "api/admin/{controller}/{id}",
            defaults: new { area = "admin", id = RouteParameter.Optional }
        );

    }
}

Add the following line of code in Application_Start() of Global.asax.cs file

GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector), new AreaHttpControllerSelector(GlobalConfiguration.Configuration));

That’s all done from the implementation aspect of WebAPI Controller in MVC 5 Areas.

Implement a HELP section for the API

In order to have a Help section for our API, install Microsoft.AspNet.WebApi.HelpPage using NuGet Package Manager Console in API Project.

PM> install-package Microsoft.AspNet.WebApi.HelpPage
Attempting to resolve dependency 'Microsoft.AspNet.WebApi.WebHost (= 5.2.2 && < 5.3.0)'.
Attempting to resolve dependency 'Microsoft.AspNet.WebApi.Core (= 5.2.2 && < 5.3.0)'.
Attempting to resolve dependency 'Microsoft.AspNet.WebApi.Client (= 5.2.2)'.
Attempting to resolve dependency 'Newtonsoft.Json (= 6.0.4)'.
Attempting to resolve dependency 'Microsoft.AspNet.Mvc (= 5.2.2 && < 5.3.0)'.
Attempting to resolve dependency 'Microsoft.AspNet.WebPages (= 3.2.2 && < 3.3.0)'.
Attempting to resolve dependency 'Microsoft.Web.Infrastructure (= 1.0.0.0)'.
Attempting to resolve dependency 'Microsoft.AspNet.Razor (= 3.2.2 && < 3.3.0)'.
Installing 'Newtonsoft.Json 6.0.5'.
Successfully installed 'Newtonsoft.Json 6.0.5'.
Installing 'Microsoft.AspNet.WebApi.Client 5.2.2'.
You are downloading Microsoft.AspNet.WebApi.Client from Microsoft, the license agreement to which is available at http://www.microsoft.com/web/webpi/eula/net_library_eula_ENU.htm. Check the package for additional dependencies, which may come with their own license agreement(s). Your use of the package and dependencies constitutes your acceptance of their license agreements. If you do not accept the license agreement(s), then delete the relevant components from your device.
Successfully installed 'Microsoft.AspNet.WebApi.Client 5.2.2'.
Installing 'Microsoft.AspNet.WebApi.Core 5.2.2'.
You are downloading Microsoft.AspNet.WebApi.Core from Microsoft, the license agreement to which is available at http://www.microsoft.com/web/webpi/eula/net_library_eula_ENU.htm. Check the package for additional dependencies, which may come with their own license agreement(s). Your use of the package and dependencies constitutes your acceptance of their license agreements. If you do not accept the license agreement(s), then delete the relevant components from your device.
Successfully installed 'Microsoft.AspNet.WebApi.Core 5.2.2'.
Installing 'Microsoft.AspNet.WebApi.WebHost 5.2.2'.
You are downloading Microsoft.AspNet.WebApi.WebHost from Microsoft, the license agreement to which is available at http://www.microsoft.com/web/webpi/eula/net_library_eula_ENU.htm. Check the package for additional dependencies, which may come with their own license agreement(s). Your use of the package and dependencies constitutes your acceptance of their license agreements. If you do not accept the license agreement(s), then delete the relevant components from your device.
Successfully installed 'Microsoft.AspNet.WebApi.WebHost 5.2.2'.
Installing 'Microsoft.AspNet.Razor 3.2.2'.
You are downloading Microsoft.AspNet.Razor from Microsoft, the license agreement to which is available at http://www.microsoft.com/web/webpi/eula/net_library_eula_ENU.htm. Check the package for additional dependencies, which may come with their own license agreement(s). Your use of the package and dependencies constitutes your acceptance of their license agreements. If you do not accept the license agreement(s), then delete the relevant components from your device.
Successfully installed 'Microsoft.AspNet.Razor 3.2.2'.
Installing 'Microsoft.AspNet.WebPages 3.2.2'.
You are downloading Microsoft.AspNet.WebPages from Microsoft, the license agreement to which is available at http://www.microsoft.com/web/webpi/eula/net_library_eula_ENU.htm. Check the package for additional dependencies, which may come with their own license agreement(s). Your use of the package and dependencies constitutes your acceptance of their license agreements. If you do not accept the license agreement(s), then delete the relevant components from your device.
Successfully installed 'Microsoft.AspNet.WebPages 3.2.2'.
Installing 'Microsoft.AspNet.Mvc 5.2.2'.
You are downloading Microsoft.AspNet.Mvc from Microsoft, the license agreement to which is available at http://www.microsoft.com/web/webpi/eula/net_library_eula_ENU.htm. Check the package for additional dependencies, which may come with their own license agreement(s). Your use of the package and dependencies constitutes your acceptance of their license agreements. If you do not accept the license agreement(s), then delete the relevant components from your device.
Successfully installed 'Microsoft.AspNet.Mvc 5.2.2'.
Installing 'Microsoft.AspNet.WebApi.HelpPage 5.2.2'.
You are downloading Microsoft.AspNet.WebApi.HelpPage from Microsoft, the license agreement to which is available at http://www.microsoft.com/web/webpi/eula/net_library_eula_ENU.htm. Check the package for additional dependencies, which may come with their own license agreement(s). Your use of the package and dependencies constitutes your acceptance of their license agreements. If you do not accept the license agreement(s), then delete the relevant components from your device.
Successfully installed 'Microsoft.AspNet.WebApi.HelpPage 5.2.2'.
Removing 'Microsoft.AspNet.WebApi.WebHost 5.0.0' from WebApi.Demo1.
Successfully removed 'Microsoft.AspNet.WebApi.WebHost 5.0.0' from WebApi.Demo1.
Removing 'Microsoft.AspNet.WebApi.Core 5.0.0' from WebApi.Demo1.
Successfully removed 'Microsoft.AspNet.WebApi.Core 5.0.0' from WebApi.Demo1.
Removing 'Microsoft.AspNet.WebApi.Client 5.0.0' from WebApi.Demo1.
Successfully removed 'Microsoft.AspNet.WebApi.Client 5.0.0' from WebApi.Demo1.
Removing 'Newtonsoft.Json 5.0.6' from WebApi.Demo1.
Successfully removed 'Newtonsoft.Json 5.0.6' from WebApi.Demo1.
Adding 'Newtonsoft.Json 6.0.5' to WebApi.Demo1.
Successfully added 'Newtonsoft.Json 6.0.5' to WebApi.Demo1.
Adding 'Microsoft.AspNet.WebApi.Client 5.2.2' to WebApi.Demo1.
Successfully added 'Microsoft.AspNet.WebApi.Client 5.2.2' to WebApi.Demo1.
Adding 'Microsoft.AspNet.WebApi.Core 5.2.2' to WebApi.Demo1.
Successfully added 'Microsoft.AspNet.WebApi.Core 5.2.2' to WebApi.Demo1.
Adding 'Microsoft.AspNet.WebApi.WebHost 5.2.2' to WebApi.Demo1.
Successfully added 'Microsoft.AspNet.WebApi.WebHost 5.2.2' to WebApi.Demo1.
Removing 'Microsoft.AspNet.Mvc 5.0.0' from WebApi.Demo1.
Successfully removed 'Microsoft.AspNet.Mvc 5.0.0' from WebApi.Demo1.
Removing 'Microsoft.AspNet.WebPages 3.0.0' from WebApi.Demo1.
Successfully removed 'Microsoft.AspNet.WebPages 3.0.0' from WebApi.Demo1.
Removing 'Microsoft.AspNet.Razor 3.0.0' from WebApi.Demo1.
Successfully removed 'Microsoft.AspNet.Razor 3.0.0' from WebApi.Demo1.
Adding 'Microsoft.AspNet.Razor 3.2.2' to WebApi.Demo1.
Successfully added 'Microsoft.AspNet.Razor 3.2.2' to WebApi.Demo1.
Adding 'Microsoft.AspNet.WebPages 3.2.2' to WebApi.Demo1.
Successfully added 'Microsoft.AspNet.WebPages 3.2.2' to WebApi.Demo1.
Adding 'Microsoft.AspNet.Mvc 5.2.2' to WebApi.Demo1.
Successfully added 'Microsoft.AspNet.Mvc 5.2.2' to WebApi.Demo1.
Adding 'Microsoft.AspNet.WebApi.HelpPage 5.2.2' to WebApi.Demo1.
Successfully added 'Microsoft.AspNet.WebApi.HelpPage 5.2.2' to WebApi.Demo1.
Uninstalling 'Microsoft.AspNet.WebApi.WebHost 5.0.0'.
Successfully uninstalled 'Microsoft.AspNet.WebApi.WebHost 5.0.0'.
Uninstalling 'Microsoft.AspNet.WebApi.Core 5.0.0'.
Successfully uninstalled 'Microsoft.AspNet.WebApi.Core 5.0.0'.
Uninstalling 'Microsoft.AspNet.WebApi.Client 5.0.0'.
Successfully uninstalled 'Microsoft.AspNet.WebApi.Client 5.0.0'.
Uninstalling 'Newtonsoft.Json 5.0.6'.
Successfully uninstalled 'Newtonsoft.Json 5.0.6'.
Uninstalling 'Microsoft.AspNet.Mvc 5.0.0'.
Successfully uninstalled 'Microsoft.AspNet.Mvc 5.0.0'.
Uninstalling 'Microsoft.AspNet.WebPages 3.0.0'.
Successfully uninstalled 'Microsoft.AspNet.WebPages 3.0.0'.
Uninstalling 'Microsoft.AspNet.Razor 3.0.0'.
Successfully uninstalled 'Microsoft.AspNet.Razor 3.0.0'.

Once successfully installed following folder structure can be found.

HelpPage

Open the HelpPageConfig.cs under AreasHelpPageApp_Start folder and uncomment the line

config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));

Now add a link API in _Layout.cshtml to get the Help Page in the Navigational Panel

<li>@Html.ActionLink("API", "Index", "Help", new { area = "" }, null)</li>

That’s it your help page is ready. Below is the sample screenshot of the page

APIhelp

Before running the API help, change the Build Property Settings of the API project. Add the below settings highlighted.

Build Property

That’s all for using Web API in MVC 5 Areas. Happy Coding….

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s