Click here to Skip to main content
15,885,216 members
Articles / Web Development / ASP.NET

ASP.NET WebAPI: Getting Started with MVC4 and WebAPI

Rate me:
Please Sign up or sign in to vote.
4.86/5 (159 votes)
12 Mar 2012CPOL12 min read 1.2M   33.8K   325  
ASP.NET Web API is a framework for building and consuming HTTP services that can reach a broad range of clients including browsers, phones and tablets.
This is an old version of the currently published article.

Introduction

I’ve spent last year working on ASP.NET MVC3 and felling good this year on MVC4. I got some new exciting features after updating to MVC4, Web API is one of those exciting features. I have read a lot on this feature and got a lot good articles on web. But I didn't get any article that covers all concepts in one place. So I decided to write an article that will cover almost all in one place with the help of other good articles.

Prerequisites 

ASP.NET MVC 4 Beta
You can also use the Web API on MVC3. Just install the WebAPI pieces using the Nuget Package Manager dialog .

Or use the Package Manager Console and type:

Install-Package AspNetWebApi

What is ASP.NET Web API? 

ASP.NET Web API is a framework for building and consuming HTTP services that can reach a broad range of clients including browsers, phones and tablets. You can use XML or JSON or something else with your API. JSON is nice for mobile apps with slow connections, for example. You can call an API from jQuery and better utilize the client's machine and browser.

In this article we will show the basic database operations (CRUD) in an HTTP service using ASP.NET Web API. Many HTTP services also model CRUD operations through REST or REST-like APIs.

Image 1

Why use ASP.NET Web API?

ASP.NET MVC was designed primarily for interacting with humans via web pages. The main use case is emitting HTML and responding to user input (submitting forms, clicking links, etc.). It does a great job at that.

ASP.NET Web API is built for all the other, non-human interactions your site or service needs to support. Think about jQuery code that's making an Ajax request, or a service interface that supports a mobile client. In these cases, the requests are coming from code and expect some kind of structured data and specific HTTP Status Codes.

These two are very complimentary, but different enough that trying to build out HTTP services using ASP.NET MVC took a lot of work to get right. The inclusion of ASP.NET Web API in ASP.NET MVC (and availability elsewhere, including ASP.NET Web Pages) means that you can build top-notch HTTP services in an ASP.NET MVC application, taking advantage of a common base and using the same underlying paradigms.

ASP.NET Web API includes support for the following features:

  • Modern HTTP programming model: Directly access and manipulate HTTP requests and responses in your Web APIs using a new, strongly typed HTTP object model. The same programming model and HTTP pipeline is symmetrically available on the client through the new HttpClient type.
  • Full support for routes: Web APIs now support the full set of route capabilities that have always been a part of the Web stack, including route parameters and constraints. Additionally, mapping to actions has full support for conventions, so you no longer need to apply attributes such as [HttpPost] to your classes and methods.
  • Content negotiation: The client and server can work together to determine the right format for data being returned from an API. We provide default support for XML, JSON, and Form URL-encoded formats, and you can extend this support by adding your own formatters, or even replace the default content negotiation strategy. Model binding and validation: Model binders provide an easy way to extract data from various parts of an HTTP request and convert those message parts into .NET objects which can be used by the Web API actions.
  • Filters: Web APIs now supports filters, including well-known filters such as the [Authorize] attribute. You can author and plug in your own filters for actions, authorization and exception handling.
  • Query composition: By simply returning IQueryable<t>, your Web API will support querying via the OData URL conventions.
  • <t> Improved testability of HTTP details: Rather than setting HTTP details in static context objects, Web API actions can now work with instances of HttpRequestMessage and HttpResponseMessage. Generic versions of these objects also exist to let you work with your custom types in addition to the HTTP types.
  • <t> Improved Inversion of Control (IoC) via DependencyResolver: Web API now uses the service locator pattern implemented by MVC’s dependency resolver to obtain instances for many different facilities.
  • <t> Code-based configuration: Web API configuration is accomplished solely through code, leaving your config files clean.  
  • <t> Self-host: Web APIs can be hosted in your own process in addition to IIS while still using the full power of routes and other features of Web API.

How To Create a New Web API Project

Start Visual Studio 2010 and follow the steps:
  1. Select New Project from the Start page/File menu then New Project.
  2. From the list of project templates: select ASP.NET MVC 4 Web Application. Select your preferred location then type your desired project name and click OK.
  3. In the New ASP.NET MVC 4 Project dialog, select Web API. The View engine will be Razor by default then click OK

Image 2

Add a Model

A model is an object that represents the data in your application. ASP.NET Web API can automatically serialize your model to JSON, XML, or some other format, and then write the serialized data into the body of the HTTP response message. As long as a client can read the serialization format, it can deserialize the object. Most clients can parse either XML or JSON. Moreover, the client can indicate which format it wants by setting the Accept header in the HTTP request message.

To see all of this in action, let's start by creating a simple model.

If Solution Explorer is not already visible, click the View menu and select Solution Explorer.

In Solution Explorer, right-click the Models folder. From the context menu, select Add then select Class.

Image 3

Name the class "Book". Next, add the following properties to the Book class.

C#
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }

Add a Repository

Our HTTP service needs to store a list of books. For purposes of this tutorial, the book list will be stored in memory.

It’s a good idea to separate this object from our service implementation. That way, we can change the backing store without rewriting the service class. This type of design is called the repository pattern. Start by defining a generic interface for the repository.

In Solution Explorer, right-click the Models folder. Select Add, then select New Item.

Add the following implementation:

C#
public interface IBookRepository
{
   IQueryable<book> GetAll();
   Book Get(int id);
   Book Add(Book item);
   void Remove(int id);
   bool Update(Book item);

}
</book>

Now add another class to the Models folder, named "BookRepository." This class will implement the IBookRespository interface. Derive the class from IBookRepository:

Add the following implementation:

C#
public class BookRepository : IBookRepository
    {
        private List<book> _books = new List<book>();
        private int _nextId = 1;

        public BookRepository()
        {
            this.Add(new Book() {Name = "MVC4 Getting Started", Price =Convert.ToDecimal(240.45)});
            this.Add(new Book() {Name = "MVC3 Getting Started", Price =Convert.ToDecimal(100.45)});
            this.Add(new Book() {Name = "Web API Getting Started", Price =Convert.ToDecimal(300.45)});
        }

        public IQueryable<book> GetAll()
        {
            return _books.AsQueryable();
        }

        public Book Get(int id)
        {
            return _books.Find(c => c.Id == id);
        }

        public Book Add(Book book)
        {
            book.Id = _nextId++;
            _books.Add(book);
            return book;
        }

        public void Remove(int id)
        {
            Book book = _books.Find(c => c.Id == id);
            _books.Remove(book);
        }

        public bool Update(Book book)
        {
            int index = _books.FindIndex(c => c.Id == book.Id);
            if (index == -1)
            {
                return false;
            }
            _books.RemoveAt(index);
            _books.Add(book);
            return true;
        }
    }
</book></book></book>

The repository keeps the books in local memory. This is OK for a tutorial, but in a real application, you would store the data externally, either a database or in cloud storage. The repository pattern will make it easier to change the implementation later.

Add a Web API Controller

If you have worked with ASP.NET MVC, then you are already familiar with controllers. In ASP.NET Web API, a controller is a class that handles HTTP requests from the client. The New Project wizard created two controllers for you when it created the project. To see them, expand the Controllers folder in Solution Explorer.

HomeController is a traditional ASP.NET MVC controller. It is responsible for serving HTML pages for the site, and is not directly related to our Web API service. ValuesController is an example WebAPI controller

Go ahead and delete ValuesController, by right-clicking the file in Solution Explorer and selecting Delete. 

Now add a new controller, as follows: 

In Solution Explorer, right-click the Controllers folder. Select Add and then select Controller.

In the Add Controller wizard, name the controller BooksController. In the Template drop-down list, select Empty API Controller. Then click Add.

Image 4

The Add Controller wizard will create a file named BooksController.cs in the Controllers folder. If this file is not open already, double-click the file to open it.

Add the following using statements:

C#
using WebAPI.Models;
using System.Net;

Add a field that holds an IBookRepository instance. 

C#
public class BooksController : ApiController
{
   static readonly IBookRepository _repository = new BookRepository();

}

Dependency Injection with IoC Containers

A dependency is an object or interface that another object requires. For example, in this article we defined a BooksController class that requires an IBookRepository instance. Above is the implementation looked like . 

This is not the best design, because the call to new the BookRepository is hard-coded into the controller class. Later, we might want to switch to another implementation of IBookRespository, and then we would need to change the implementation of BooksController. It is better if the BooksController is loosely decoupled from any concrete instance of IBookRespoitory.

Dependency injection addresses this problem. With dependency injection, an object is not responsible for creating its own dependencies. Instead, the code that creates the object injects the dependency, usually through a constructor parameter or a setter method.

Here is a revised implementation of BooksController:

C#
public class BooksController : ApiController
{
    static IBookRepository _repository;
    public BooksController(IBookRepository repository)
    {
        if (repository == null)
        {
            throw new ArgumentNullException("repository");
        }
        _repository = repository;
    }
}

An IoC container is a software component that is responsible for creating dependencies. IoC containers provide a general framework for dependency injection. If you use an IoC container, then you don’t need to wire up objects directly in code. Several open-source .Net IoC containers are available. The following example uses Unity, an IoC container developed by Microsoft patterns & practices

In Solution Explorer, double-click Global.asax. Visual Studio will open the file named Global.asax.cs file, which is the code-behind file for Global.asax. This file contains code for handling application-level and session-level events in ASP.NET. 

Add a static method named RegisterDependencies to the WebApiApplication class.

Add the following using statements:

C#
using Microsoft.Practices.Unity;
using WebAPI.Models;

Add the following implementation:

C#
private void RegisterDependencies()
        {
            IUnityContainer container = new UnityContainer();
            container.RegisterInstance<ibookrepository>(new BookRepository());
            GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
                t =>
                    {
                        try
                        {
                            return container.Resolve(t);
                        }
                        catch (ResolutionFailedException)
                        {
                            return null;
                        }
                    },
                t =>
                    {
                        try
                        {
                            return container.ResolveAll(t);
                        }
                        catch (ResolutionFailedException)
                        {
                            return new List<object>();
                        }
                    });

        }
</ibookrepository>

Now modify the Application_Start method to call RegisterDependencies:

C#
protected void Application_Start()
        {
            RegisterDependencies();

            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);

            BundleTable.Bundles.RegisterTemplateBundles();
        }

The Unity methods Resolve and ResolveAll map almost directly to GetService and GetServices methods. However, the Unity methods throw an exception if the type cannot be resolved.

Getting Book

The book service will expose two "read" methods: one that returns a list of all books, and another that looks up a book by ID. The method name starts with "Get", so by convention it maps to GET requests. Further, the method has no parameters, so it maps to a URI with no "id" segment in the path. The second method name also starts with "Get", but the method has a parameter named id. This parameter is mapped to the "id" segment of the URI path. The ASP.NET Web API framework automatically converts the ID to the correct data type (int) for the parameter.

Notice that GetBook throws an exception of type HttpResponseException if id is not valid. This exception will be translated by the framework into a 404 (Not Found) error.

C#
public IEnumerable<book> GetAllBooks()
        {
            return _repository.GetAll();
        }

        public Book  GetBook(int id)
        {
            Book book= _repository.Get(id);
            if (book == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
            return book;
        }
</book>
Image 5
Image 6

Creating a Book

To create a new book, the client sends an HTTP POST request, with the new contact in the body of the request message.

Here is a simple implementation of the method:

C#
public Book PostBook(Book book)
  {
     book = _repository.Add(book);
     return book;
  }

To handle POST requests, we define a method whose name starts with "Post...". The method takes a parameter of type Book. By default, parameters with complex types are deserialized from the request body. Therefore, we expect the client to send us a serialized representation of a Book object, using either XML or JSON for the serialization.

This implementation will work, but it is missing a couple of things to complete.

  • Response code: By default, the Web API framework sets the response status code to 200 (OK). But according to the HTTP/1.1 protocol, when a POST request results in the creation of a resource, the server should reply with status 201 (Created).
  • Location: When the server creates a resource, it should include the URI of the new resource in the Location header of the response.

ASP.NET Web API makes it easy to manipulate the HTTP response message. Here is the improved implementation:

C#
  public HttpResponseMessage<book> PostBook(Book book)
        {
            book = _repository.Add(book);
            var response = new HttpResponseMessage<book>(book,
                HttpStatusCode.Created);

            string uri = Url.Route(null, new { id = book.Id });
            response.Headers.Location = new Uri(Request.RequestUri, uri);
            return response;
        }
</book></book>

Notice that the method return type is now HttpResponseMessage<book>. The HttpResponseMessage<t> class is a strongly typed representation of an HTTP response message. The generic parameter T gives the CLR type that will be serialized to the message body.

In the constructor, we specify the Book instance to serialize and the HTTP status code to return:

C#
var response = new HttpResponseMessage<book>(book, HttpStatusCode.Created);
</book>

The HttpResponseMessage<t> class also lets you manipulate the response headers. In this case, we set the Location header to the URI of the new book: 

C#
string uri = Url.Route(null, new { id = book.Id });
            response.Headers.Location = new Uri(Request.RequestUri, uri);
            return response;
Image 7

Updating a Book

Updating a book with PUT is straightforward. Simply define a method whose name starts with "Put...":

C#
public void PutBook(int id, Book book)
        {
            book.Id = id;
            if (!_repository.Update(book))
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
        }

This method takes two parameters, the book ID and the updated book. The id parameter is taken from the URI path, and the book parameter is deserialized from the request body. By default, the ASP.NET Web API framework takes simple parameter types from the route and complex types from the request body.

Deleting a Book

To delete a book, define a "Delete..." method.

C#
public HttpResponseMessage DeleteBook(int id)
        {
            _repository.Remove(id);
            return new HttpResponseMessage(HttpStatusCode.NoContent);
        }

According to the HTTP specification, the DELETE method must be idempotent, meaning that several DELETE requests to the same URI must have the same effect as a single DELETE request. Therefore, the method should not return an error code if the contact was already deleted.

If a DELETE request succeeds, it can return status 200 (OK) with an entity-body that describes the status, or status 202 (Accepted) if the deletion is still pending, or status 204 (No Content) with no entity body. In this example, the method returns status 204.

Using the HTTP Service with Javascript, jQuery and jQuery Template

In Solution Explorer, expand the Views folder, and expand the Home folder under that. You should see a file named Index.cshtml. Double-click this file to open it.
Add the following code:  

JavaScript
<script type="text/javascript">
    $(function() {
        $.getJSON(
            "api/books",
            function(data) {
                $.each(data,
                    function(index, value) {
                        $("#bookTemplate").tmpl(value).appendTo("#books");
                    }
                );
                $("#loader").hide();
                $("#addBook").show();
            }
        );

        $("#addBook").submit(function() {
            $.post(
                "api/books",
                $("#addBook").serialize(),
                function(value) {
                    $("#bookTemplate").tmpl(value).appendTo("#books");
                    $("#name").val("");
                    $("#price").val("");
                },
                "json"
            );
            return false;
        });
        $(".removeBook").live("click", function() {
            $.ajax({
                type: "DELETE",
                url: $(this).attr("href"),
                context: this,
                success: function() {
                    $(this).closest("li").remove();
                }
            });
            return false;
        });
        $("input[type=\"submit\"], .removeBook, .viewImage").button();
    });
    function find() {
        var id = $('#bookId').val();
        $.getJSON("api/books/" + id,
            function(data) {
                var str = data.Name + ': $' + data.Price;
                $('#book').html(str);
            })
            .fail(
                function(jqXHR, textStatus, err) {
                    $('#book').html('Error: ' + err);
                });
    }

</script>
Image 8
Image 9

The above examples demonstrate Get all books, Add Book and Remove a Book from the list. The find function helps us to get a book by ID. For binding all the book list we used jQuery template over here. Following portion also needed for that:

HTML
<script id="bookTemplate" type="text/html"> 
        <li> Book Name: ${ Name }
              Price:${ Price }  
                <a class="button small red removeBook" href="$%7B%20Self%20%7D">
                    Remove</a>

        </li>
    </script>

And the html should look like the following:

HTML
<div class="grid_16 body-container">
            <div class="margin grid_6 alpha">
                <label for="Name">
                    Name</label>

                <input type="text" class="text grid_4" name="Name" id="name" />
                

                <label for="Price">
                    Price</label>

                <input type="text" class="text grid_4" name="Price" id="price" />
                

                <input type="submit" class="button small green" value="Add" />
                

                <label for="bookId">
                    Serach By ID
                </label>
                

                <input type="text" class="text grid_4" size="20" id="bookId" />
		

                <input type="button" class="button small gray"  önclick="find();" value="Search" />
                

                

               
                 <label id="book">
                </label>
            </div>
            <div class="grid_8 omega">
                <ul class="books" id="books">
                </ul>
            </div>
        </div>
Please download source code for more detail.

Points of Interest

  1. Full Support For Routes
  2. Model Binding
  3. Filters
  4. Content Negotiation
  5. Bundling by default
  6. oData style query support
  7. Razor Enhancements
  8. URL Resolution - Support for ~/ syntax
  9. Conditional attribute rendering
  10. NuGet Based Project Installation
and many more...

History

I have taken all the concepts from the following:

License

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


Written By
Software Developer (Senior)
Singapore Singapore
A life-long-learner, maker and soft music fan. Likes building things to solve problems. Years of successful records serving mid and large scale .NET applications in domestic and international client environment. Expertise in different areas of software development life cycles and Software Architecture.

Always looks for new technology and loves to get hands dirty Smile | :)

Comments and Discussions

Discussions on this specific version of this article. Add your comments on how to improve this article here. These comments will not be visible on the final published version of this article.