Introduction to .NET Web API 2 with C# Part 1

Introduction

Web API has been around for some years now. It is a very efficient and lightweight technology to build RESTful web services in .NET. Web API is very similar to .NET MVC with its controllers and routing rules. Therefore if you are familiar with MVC then it’s not too difficult to get going with Web API either.

If you are not familiar with Web API at all then I suggest that you first go through the basics using the link provided above. There are some good courses available there as well.

In this short series on Web API 2 we’ll discuss some of the new options available compared to the “original” Web API package.

Demo

I’m building the demo using Visual Studio 2013 Express for Web. You can probably do the same in VS 2012 by downloading the MVC 5 templates. So open VS2013 and create a new ASP.NET web project called RockbandData:

Web API 2 new project

Click OK and then select the Web API template:

Web API 2  template

Notice that it says ‘No authentication’ on the right side of the window. We’ll change that so click Change Authentication. Select the Individual User Accounts option in the Change Authentication window and click OK. Click OK again on the New ASP.NET Project window so that Visual Studio can create the project. It takes some time but eventually you’ll have a project structure like this:

Default project structure Web API 2

You can immediately run the project by pressing F5 and you’ll see a default web site popping up in your browser:

Web API 2 default homepage

It looks like a normal MVC website. However, there are some differences between a “real” MVC default website and its Web API counterpart. Even if we stated that we require authentication there’s no link to log in or sign up new users anywhere. The reason is that the standard MVC AccountController controller is not available. User login and signup is handled in a slightly different way which we’ll explore later.

Documentation page

What’s new is a link that says ‘API’ up in the navigation bar. Click on it and you’ll see that a list of possible URLs appears in a table:

Web API 2 default help page

This is a documentation page for the available URL endpoints of the web service. Click on one of the URLs and you’ll see an example for the JSON and XML response structures:

Sample return values

If you check out one of the POST urls then the documentation will show you the expected request body format as well:

RequestBodyDocumentation

The documentation is provided by a pre-installed NuGet package: Microsoft.AspNet.WebApi.HelpPage. It will look for ApiControllers in your project and any documentation available on them using Reflection. You won’t find an assembly reference to this package under the References section so don’t look for it. However, there’s a HelpPage area in the solution:

Web API 2 Help Page area

The controllers, models and views in this area help construct the Documentation page we’ve just seen. Any new API controller you create will be added to the documentation. You can add comments in the usual way: just decorate the Controller with VS XML comments:

/// <summary>
/// This the Get all values method within the ValuesController
/// </summary>
/// <returns>A sequence of strings</returns>
public IEnumerable<string> Get()
{
     return new string[] { "value1", "value2" };
}

Also, you need to let VS generate an XML documentation file:

Add XML documentation to page

We’re not done yet. Locate the HelpPageConfig.cs file in the Areas/HelpPage/App_Start folder. There will be some code already but it’s all commented out. Uncomment the first line and modify the file name to match what you provided in the previous step:

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

Rebuild and run the project. If you get a runtime exception saying that the documentation XML file wasn’t found then check the file URL again in the code we’ve just commented out. It may have been overwritten. The documentation page should show the new description:

Description updated

Values controller

You can call the values controller in the browser to check if the Get() method is working. The URL should be http://localhost:50170/api/values. The port number in your case may of course differ. At first you should get an error message saying that the request was unauthorised. That’s because the ValuesController has been decorated with the [Authorize] attribute. Uncomment it and refresh the page. You should see an XML with the default values returned.

Model data

Any demo on the Web API is futile without a data source. Let’s build a class hierarchy in the Models folder. Note that your domains should exist in a separate project of course but this post is not about layered architecture but the Web API. If you are interested in layered projects then check out the series on Domain Driven Design and/or SOA.

Also, to avoid wasting too much time setting up the data source in e.g. Entity Framework or MongoDb we’ll be working with in-memory data.

Locate the Models folder and add a new folder in it called Domain. We’ll build a simple data hierarchy around rock music. Add the following classes to the Domain folder:

public class Prize
{
    public string Name { get; set; }
}
public class Album
{
    public string Title { get; set; }
    public int Year { get; set; }
}
public class RockBand
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<Album> Albums { get; set; }
    public ICollection<Prize> Prizes { get; set; }
}

Next we’ll set up the in-memory repository to get some data to start with. Again, all this is violating every principle behind SOLID and layered architectures but I don’t want to get sidetracked. We’ll set up an in-memory data context which has the same function as the DbContext object you might be familiar with from EntityFramework. We’ll use the singleton pattern to get hold of the object context instance.

Insert a new folder called Repository. Add the following object context “simulation”:

public class InMemoryDatabaseObjectContext
{
        private List<RockBand> _rockBands;

        public InMemoryDatabaseObjectContext()
        {
            _rockBands = InitialiseRockBands();
        }
        
        public IEnumerable<RockBand> GetAll()
        {
            return _rockBands;
        }

        public static InMemoryDatabaseObjectContext Instance
        {
	     get
	     {
		   return Nested.instance;
	     }
        }

        private class Nested
        {
	     static Nested()
	     {
	     }
	     internal static readonly InMemoryDatabaseObjectContext instance = new InMemoryDatabaseObjectContext();
        }

        private List<RockBand> InitialiseRockBands()
        {
            List<RockBand> rockbands = new List<RockBand>();

            RockBand greatBand = new RockBand();
            greatBand.Name = "Great band";
            greatBand.Id = 1;
            greatBand.Albums = new List<Album>(){new Album(){Title = "First album", Year = 2000}, new Album(){Title = "Second album", Year = 2003}
                , new Album(){Title = "Third album", Year=2005}};
            greatBand.Prizes = new List<Prize>() { new Prize() { Name = "Best band" }, new Prize(){Name = "Best newcomers"} };

            RockBand rockBand = new RockBand();
            rockBand.Name = "Funny band";
            rockBand.Id = 2;
            rockBand.Albums = new List<Album>(){new Album(){Title = "Debut", Year = 1979}, new Album(){Title = "Continuation", Year = 1980}
                , new Album(){Title = "New Year", Year=1982}, new Album(){Title ="Summer", Year=1985}};
            rockBand.Prizes = new List<Prize>() { new Prize() { Name = "Cool band" }, new Prize() { Name = "Best band" }, new Prize(){Name = "First choice"} };

            RockBand anotherBand = new RockBand();
            anotherBand.Name = "Sounds good";
            anotherBand.Id = 3;
            anotherBand.Albums = new List<Album>(){new Album(){Title = "The beginning", Year = 1982}, new Album(){Title = "The end", Year = 1986}};
            anotherBand.Prizes = new List<Prize>() {new Prize() { Name = "First choice" } };

            RockBand rb = new RockBand();
            rb.Name = "Sounds good";
            rb.Id = 4;
            rb.Albums = new List<Album>() { new Album() { Title = "Cool", Year = 1988 }, new Album() { Title = "Yeah", Year = 1989 }
                , new Album() { Title = "Oooooohhh", Year = 1990 }, new Album() { Title = "Entertain", Year = 1991 }, new Album() { Title = "Go home", Year = 1992 }};
            rb.Prizes = new List<Prize>() { new Prize() { Name = "First choice" }, new Prize() { Name = "Cool band" } };

            rockbands.Add(greatBand);
            rockbands.Add(rockBand);
            rockbands.Add(anotherBand);
            rockbands.Add(rb);

            return rockbands;
        }
}

Add the following components necessary for the thread safe lazy singleton pattern:

public interface IObjectContextFactory
{
        InMemoryDatabaseObjectContext Create();
}
public class LazySingletonObjectContextFactory : IObjectContextFactory
{
	public InMemoryDatabaseObjectContext Create()
	{
		return InMemoryDatabaseObjectContext.Instance;
	}
}

If you don’t know what these components mean the check out the link provided above about the singleton pattern. You can even ignore it for now as they are not important for the main discussion: the lazy singleton pattern will make sure that the data source will not be wiped out with every new HTTP request and that all requests will query the same object context. You’ll also see that we have some initial data with a couple of great rock bands.

We have our data store so we’d like to extract the rockband data via an API call.

Rockbands controller

Right click the Controllers folder and select Add and then Controller from the context menu. The Add Scaffold window will appear. Select the Web API 2 Controller – Empty option and click Add. In the Add Controller window insert the controller name: RockbandsController. The new controller will derive from ApiController as expected.

Add the following private field and constructor to the controller:

private IObjectContextFactory _objectContextFactory;

public RockbandsController()
{
     _objectContextFactory = new LazySingletonObjectContextFactory();
}

Another warning: DON’T DO THIS AT HOME! In a real project you’ll not consult the repository directly from a controller and you will inject the correct implementation of the IObjectContextFactory dependency through the controller constructor. If you’re wondering how to do this check out the links on DDD and SOLID provided above.

Insert the following Get() method into the controller:

public IEnumerable<RockBand> Get()
{
            return _objectContextFactory.Create().GetAll();
}

This method will be invoked if a GET request is directed to the RockbandsController using the URL /api/rockbands. Run the application and enter this URL into the browser: http://localhost:%5Byour port number]/api/rockbands. You should get an XML representation of the sequence of Rockbands:

Get all rockbands xml

Great! We have now the basis for moving forward. We’ll continue our investigation of the Web API in the next part.

View the list of MVC and Web API related posts here.

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

6 Responses to Introduction to .NET Web API 2 with C# Part 1

  1. Larisa says:

    Excellent. Thank you very much.

  2. Leong C. Chai says:

    I am newbie to all this, I followed the steps and got stuck at this line:
    Insert a new folder called Repository. Add the following object context “simulation”:

    Is the Repository folder created in the Domain folder? Or at the same level as Domain folder?
    How do I add object context? I choose Add… New Item… but can’t find anything named ‘Object Context’

    Please help. Thank you.

  3. syedwasti says:

    Great one Andras!
    Thanks for being so helpful. No latest articles though??

    Greetings from Sollentuna

    • Andras Nemes says:

      Hello, thanks for your comment. There’s a link at the end of the article (“next part”) which leads you to the next installment. Hälsningar från Häggvik, Andras

  4. Uros says:

    Great article. Constructor for InMemoryDatabaseObjectContext should be private?

Leave a reply to Leong C. Chai Cancel reply

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.