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
- Visual Studio 2013
- Unit Testing using SOAP UI
- Knowledge on how to use NuGet Packages to update your solution
- 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);
Step 4: WebApiConfig.cs
Add the following line in the Register(HttpConfiguration config)
method to enable Web API 2.
config.MapHttpAttributeRoutes();
Building a data class and domain specific models
Step 5: Create a VehicleInfo
data class.
namespace VehicleStore.Models
{
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
{
private List<VehicleInfo> vehicles = new List<VehicleInfo>();
public VehicleRepository()
{
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;
}
vehicles.RemoveAt(index);
vehicles.Add(item);
return true;
}
public VehicleInfo Add(VehicleInfo item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
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
{
static readonly IVehicleRepository repository = new VehicleRepository();
[Route("vehicles")]
[HttpGet]
public IEnumerable<vehicleinfo> GetAllVehicles()
{
return repository.GetAll();
}
[Route("vehicles/{Id:guid}", Name="VehicleInfoById")]
public VehicleInfo GetVehicleInfo(Guid Id)
{
VehicleInfo item = repository.Get(Id);
if (item == null)
{
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