Introduction
I’ve spent the last year working on ASP.NET MVC3 and feeling 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 have read a lot of good articles on the web. But I didn't get any article that covers all
the concepts in one place. So I tried to combine those in one place for beginners. Please don't consider this article as my own invention..
everything is taken from several articles. Please navigate the links in the History section for further details.
Prerequisites
- ASP.NET MVC 4
- 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.
Why use ASP.NET Web API?
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. - 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. - 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.
- Code-based configuration: Web API configuration is accomplished solely through code, leaving your config files clean.
- 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:
- Select New Project from the Start page/File menu then New Project.
- 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.
- In the New ASP.NET MVC 4 Project dialog, select Web API. The View engine will be Razor by default then click OK.
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 others. Then serialized and write those data into the body of HTTP response message. As long as a client can read the serialization format, it can deserialize the object.
Most of the clients are able to parse XML or JSON. By setting the Accept header in the HTTP request message the client can indicate which format it wants.
We will prove the above concepts step by step. Let's start by creating a simple model.
In Solution Explorer, right-click the Models folder then select Add then select Class.
Name the class Book
. Next, add the following properties to the
Book
class.
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
Add a Repository
For serving our article's purposes, let's store the list in memory and the HTTP service needs to store a list of books.
Let's separate the book object from our service implementation. This is because we can change the backing store without rewriting the service class.
This type of design is called the Repository pattern. For this purpose, we need a generic interface. Let's see the following steps to know how to define a generic interface for a book repository.
In Solution Explorer, right-click the Models folder. Select Add, then select New Item.
Then add another class to the Models folder, named "BookRepository
" which will implement the IBookRespository
interface derived from IBookRepository
:
Following are the implementations:
public interface IBookRepository
{
IEnumerable<book> GetAll();
Book Get(int id);
Book Add(Book item);
void Remove(int id);
bool Update(Book item);
}
public class BookRepository : IBookRepository
{
private BookStore db = new BookStore();
public BookRepository()
{
}
public IEnumerable<Book> GetAll()
{
return db.Books;
}
public Book Get(int id)
{
return db.Books.Find(id);
}
public Book Add(Book item)
{
db.Books.Add(item);
db.SaveChanges();
return item;
}
public void Remove(int id)
{
Book book = db.Books.Find(id);
db.Books.Remove(book);
db.SaveChanges();
}
public bool Update(Book item)
{
db.Entry(item).State = EntityState.Modified;
db.SaveChanges();
return true;
}
}
The repository will keep books in local memory. We already mentioned and we can compromise for
the article purpose but in a real application please don't do it. Because you need to store data either in 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.
As we want to start from the scratch, go ahead and delete ValuesController.
Do we need to mention how to delete? OK, right-click the file in Solution Explorer and select 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 dropdown list, select Empty API Controller. Then click Add.
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 and add a field for holding an
IBookRepository
instance.
using WebAPI.Models;
using System.Net;
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 what the implementation looks like. p;p;
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
:
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, 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 ConfigureApi
to the WebApiApplication
class.
Add the following using
statements:
using Microsoft.Practices.Unity;
using WebAPI.Models;
Add the following implementation:
void ConfigureApi(HttpConfiguration config)
{
var unity = new UnityContainer();
unity.RegisterType<BooksController>();
unity.RegisterType<IBookRepository, BookRepository>(
new HierarchicalLifetimeManager());
config.DependencyResolver = new IoCContainer(unity);
}
Now modify the Application_Start
method to call RegisterDependencies
:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ConfigureApi(GlobalConfiguration.Configuration);
Database.SetInitializer(new BookInitializer());
}
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
the ID is not valid. This exception will be translated
by the framework into a 404 (Not Found) error.
public IEnumerable<book> GetAllBooks()
{
return _repository.GetAll();
}
public Book GetBook(int id)
{
Book book = _repository.Get(id);
if (book == null)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
return book;
}
Creating a Book
To create a new book, the client sends an HTTP POST request, with the new book in the body of the request message.
Here is a simple implementation of the method:
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:
public HttpResponseMessage PostBook(Book book)
{
book = _repository.Add(book);
var response = Request.CreateResponse<Book>(HttpStatusCode.Created, book);
string uri = Url.Route(null, new { id = book.Id });
response.Headers.Location = new Uri(Request.RequestUri, uri);
return response;
}
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. This was in the Beta version. You will get compilation errors. The new way to handle this is via the
Request
property in your controllers: you will need to change any return type from
HttpResponseMessage<t>
to HttpResponseMessage
.
In the constructor, we specify the Book
instance to serialize and the HTTP status code to return:
var response = Request.CreateResponse<Book>(HttpStatusCode.Created, book);
Updating a Book
Updating a book with PUT is straightforward. Simply define a method whose name starts with "Put...":
public void PutBook(int id, Book book)
{
book.Id = id;
if (!_repository.Update(book))
{
throw new HttpResponseException(new HttpResponseMessage(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.
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 book 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:
<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>
The presentation of data on the View looks like the following:
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 lists we used the jQuery template over here.
The following portion also needed for that:
<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:
<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>
We have used the jQuery template for making the right site book list. Please download
the source code for more details.
Points of Interest
- Full support for Routes
- Model binding
- Filters
- Content negotiation
- Bundling by default
- oData style query support
- Razor enhancements
- URL resolution - Support for ~/ syntax
- Conditional attribute rendering
- NuGet based project installation
and many more...
History
I have taken all the concepts/a few contents from the following:
Hope you all enjoyed what I described. Thanks for reading the article. Have fun!