Click here to Skip to main content
Click here to Skip to main content

Build and deploy ASP.NET Web API 2 service layer at the speed of thought!

By , 17 Feb 2014
Rate this:
Please Sign up or sign in to vote.

Download source 

Introduction

Lately I had to use Web API 2 for one of my projects. The need was to deploy a quick-n-dirty beta version into production and subsequently iterate it to fix bugs and data issues. I was looking for an article that would help me, take it from development onto production, but was unable to find one. The objective of this article is to attempt deploy a simple Web API 2 service onto production under 60 minutes. I strongly recommend you to refer to the quick references section for in-depth explanation of Web API and all the nitty-gritty details.

Pre-requisites

  1. Visual Studio 2013
  2. Unit Testing using SOAP UI
  3. Knowledge on how to use NuGet Packages to update your solution
  4. Willingness to see through all the technical issues

Quick References

(Must read for in-depth understanding, however its not required for this article)

First things First 

Step 1: Start Visual Studio 2013. From the File menu, select New and then select Project.

In the Templates pane, select Installed Templates and expand the Visual C# node. Under Visual C#, select Web, select Visual Studio 2012. In the list of project templates, select ASP.NET MVC 4 Web Application.

Name the project "VehicleStore". See screenshot below.

 

In the New ASP.NET MVC 4 Project dialog, select the Web API template. Check the create a unit test project checkbox. Click OK. See screenshot below.

 

Step 2: Manage NuGet Packages From the Tools menu, select 'NuGet Package Manager' and then select 'Manage NuGet Packages with Solution...' To read more about NuGet, refer to this link for more information - Installing NuGet. Install all the ASP.NET Web API 2.1 NuGet packages as shown in the screenshot below. 

Configuration Files

Step 3: Global.asax

Add the following line in the Application_Start() method to enable Web API 2.

            GlobalConfiguration.Configure(WebApiConfig.Register);

            // NOTE: This commented code will NOT work for Web API 2.0.
            //WebApiConfig.Register(GlobalConfiguration.Configuration);
Step 4: WebApiConfig.cs

Add the following line in the Register(HttpConfiguration config) method to enable Web API 2.

   
            // Add this line for Web API 2 - Attribute Routing
            config.MapHttpAttributeRoutes();

Building a data class and domain specific models

Step 5: Create a VehicleInfo data class.

  
 
namespace VehicleStore.Models
{
    /// <summary>
    /// A Vehicle Info data class.
    /// </summary>
    public class VehicleInfo
    {
        public Guid Id { get; set; }
        public string Trim { get; set; }
        public string Make { get; set; }
        public string Model { get; set; }
        public int Year { get; set; }
        public System.Drawing.Color Color { get; set; }
        public string VIN { get; set; }
        public int SafetyRating { get; set; }
        public int EPAGreenScore { get; set; }
        public int Axles { get; set; }
     }
}

Step 6: Create a IVehicleRepository interface

  
 
namespace VehicleStore.Models
{
    interface IVehicleRepository
    {
        IEnumerable<VehicleInfo> GetAll();
        IEnumerable<VehicleInfo> GetVehiclesByYear(int year);
        IEnumerable<VehicleInfo> GetVehiclesByMake(string make);
        VehicleInfo Get(Guid Id);
        void Remove(Guid Id);
        bool Update(VehicleInfo item);
        VehicleInfo Add(VehicleInfo item);
    }
}

Step 7: Create a Vehicle repository concrete implementation.

NOTE: There are several ways to implement this, for the sake of clarity it has been simplified. The actual production implementation might use a data store like SQL server or an Entity Framework layer in between.

  

namespace VehicleStore.Models
{
    public class VehicleRepository : IVehicleRepository
    {
        /// <summary>
        /// This is not the right approach for production quality code. 
        /// This is for demo purposes only.
        /// </summary>
        private List<VehicleInfo> vehicles = new List<VehicleInfo>();

        public VehicleRepository()
        {
            // This is used to populate initial data.
            // NOTE: This is not the right approach for production quality code.
            PopulateInitialData();
        }
        
        public IEnumerable<VehicleInfo> GetAll()
        {
            return vehicles;
        }

        public IEnumerable<VehicleInfo> GetVehiclesByYear(int year)
        {
            return vehicles.FindAll(p => p.Year == year);
        }

        public IEnumerable<VehicleInfo> GetVehiclesByMake(string make)
        {
            return vehicles.FindAll(p => p.Make == make);
        }

        public VehicleInfo Get(Guid Id)
        {
            return vehicles.Find(p => p.Id == Id);
        }

        public void Remove(Guid Id)
        {
            vehicles.RemoveAll(p => p.Id == Id);
        }

        public bool Update(VehicleInfo item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            int index = vehicles.FindIndex(p => p.Id == item.Id);
            if (index == -1)
            {
                return false;
            }
            
            // This form of update is to remove and add again.
            vehicles.RemoveAt(index);
            vehicles.Add(item);
            
            return true;
        }

        public VehicleInfo Add(VehicleInfo item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            // A new GUID is assigned every time a new item is added.
            item.Id = System.Guid.NewGuid();
            vehicles.Add(item);
            return item;
        }

        private void PopulateInitialData()
        {
            Add(new VehicleInfo { Make = "Infiniti", Model = "FX 35", Year = 2009, Trim = "Crossover" });
            Add(new VehicleInfo { Make = "Toyota", Model = "RAV44", Year = 2005, Trim = "Sport" });
            Add(new VehicleInfo { Make = "Audi", Model = "A5", Year = 2010, Trim = "Convertible" });
        }
    }
} 

Step 8: The Controller class - VehiclesController

Delete the standard ValuesController class and create the new VehiclesController.cs

NOTE: The VehiclesController inherits from ApiController class.

Points to note

  • The attribute Route is used in Web API 2.0. It defines attributed based Routing. Routing is how Web API matches a URI to an action. Complex URI Routing can be unambiguously defined using the attribute Route.
  • The attribute Route defines the actual incoming URI. Its followed by the HTTP Verb attribute (for example: [HttpGet]).
  • Route Constraints - This attribute [Route("vehicles/{Id:guid}", Name="VehicleInfoById")] for instance specifies that a request with Id as Guid would retrieve a particular vehicle with that value. Route constraint can act as a filter to route requests correctly. 
  • Refer to the list of Route Constraints
  

namespace VehicleStore.Controllers
{
    public class VehiclesController : ApiController
    {
        // This is NOT a correct implementation. This is for demonstration purposes only.
        // NOTE: NOT for PRODUCTION USE.
        static readonly IVehicleRepository repository = new VehicleRepository();

        [Route("vehicles")]
        [HttpGet]
        public IEnumerable<vehicleinfo> GetAllVehicles()
        {
            return repository.GetAll();
        }

        /// <summary>
        /// The route attribute with the data type of GUID
        /// </summary>
        /// <param name="Id" />
        /// <returns>
        [Route("vehicles/{Id:guid}", Name="VehicleInfoById")]
        public VehicleInfo GetVehicleInfo(Guid Id)
        {
            VehicleInfo item = repository.Get(Id);

            if (item == null)
            {
                //NOTE: Uncomment the following line of code to use HttpError object.
                //var message = string.Format("Product with id = {0} not found", id);
                //HttpError err = new HttpError(message);
                //return Request.CreateResponse(HttpStatusCode.NotFound, err);

                var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
                {
                    Content = new StringContent(string.Format("No vehicle with ID = {0} exists", Id)),
                    ReasonPhrase = "The given VehicleID was not found."
                };
                throw new HttpResponseException(resp);
            }

            return item;
        }

        [Route("vehicles/{make:alpha}")]
        public IEnumerable<vehicleinfo> GetVehiclesByMake(string make)
        {
            return repository.GetAll().Where(
                p => string.Equals(p.Make, make, StringComparison.OrdinalIgnoreCase));
        }

        [Route("vehicles")]
        [HttpPost]
        public HttpResponseMessage PostVehicle(VehicleInfo item)
        {
            item = repository.Add(item);
            var response = Request.CreateResponse<vehicleinfo>(HttpStatusCode.Created, item);

            string uri = Url.Link("VehicleInfoById", new { id = item.Id });
            response.Headers.Location = new Uri(uri);
            return response;
        }

        [Route("vehicles")]
        [HttpPut]
        public void PutVehicle(Guid Id, VehicleInfo item)
        {
            item.Id = Id;
            if (!repository.Update(item))
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
        }

        [Route("vehicles")]
        [HttpDelete]
        public void DeleteVehicle(Guid Id)
        {
            VehicleInfo item = repository.Get(Id);
            if (item == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            repository.Remove(Id);
        }
    }
}

</vehicleinfo></vehicleinfo></returns></vehicleinfo>
Step 9: Build the solution.

Step 10: Publish the solution to an external folder.

Right-click on the VehicleStore project and select "Publish..." 

In the following screen, choose 'Folder' as the publish option and specify a local folder.

Step 11: Local Deployment: Open IIS Manager and create a new a web site for the Web API 2.0 project. The Port 8080 is being used instead of 80. See the screenshot for the actual configuration. 

Notice the Application Pool DefaultAppPool settings in the following screen shot. Choose the Identity as LocalSystem

Step 12: Unit Testing using SOAP UI  

Create a new REST project in SOAP UI. Observe the end point in the screenshot below.

Conclusion

In this article, we created a Model, a Model repository and the Web API 2 configuration goes into the VehiclesController which inherits from ApiController. The rest of the configuration goes into Global.asax and the webApiConfig.cs file.

The entire code (Visual Studio 2013 solution) is available for download. Let me know your feedback and I shall update the article accordingly.

Further Reading

This is an article from my ASP.NET mentor Chander Dhall. There are two parts to this article, I strongly recommend you to read both the parts.

Restful API Best Practices

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Naveen Karamchetti
Architect AT&T Wi-Fi Services
United States United States
Naveen has done his Masters (M.S.) in Computer science, has started his career programming the mainframes and now has more than a decade of programming, development and design experience. Naveen has a sharp eye and keen observation skills. Naveen has worked for several companies and strived hard to build large scale business applications and bringing better solutions to the table.
Quite recently Naveen has built a fairly complex integration platform for a large bank. His hobbies include training, mentoring and research. Naveen spends his free time visiting National Parks nationwide.
 
Naveen has developed the BizTalk Control Center (BCC)
http://biztalkcontrolcenter.codeplex.com

Comments and Discussions

 
QuestionClarification? Pinmembertonofsteel118-Feb-14 17:19 
AnswerRe: Clarification? PinmemberJarmoM24-Feb-14 0:58 
QuestionRepository question Pinmemberdsmportal11-Feb-14 5:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140415.2 | Last Updated 17 Feb 2014
Article Copyright 2014 by Naveen Karamchetti
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid