Create and Consume ASP.Net Web API REST Services - MVC4

7890
Views
144
5
144

There is a lot of buzz about Web API. So what is it?

ASP.NET Web API is a framework that makes it easy to build HTTP services that reach a broad range of clients, including browsers and mobile devices. ASP.NET Web API is an ideal platform for building RESTful applications on the .NET Framework.

Okay.. so??? Hmmmm... I cant say more than this because I am really bad at explaining things!!.. Lets see what MVC4 release notes say about it..

ASP.NET Web API

ASP.NET MVC 4 now includes ASP.NET Web API, a new framework for creating HTTP services that can reach a broad range of clients including browsers and mobile devices. ASP.NET Web API is also an ideal platform for building RESTful services.

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, 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.

For more details on ASP.NET Web API please visit www.asp.net/web-api.

Enough!!! Show me how to Create and Consume!!

Here we Go!!

The scene: We are going to create an Web -Api application which provides data of orders for the Client.

Web Api Application

  1. Start Visual studio and select Asp.net MVC4 Web Application
  2. Give it a name "Orders"
  3. Now Select Web API project templete and click ok.

Now Lets create a model for the Orders under Model folder

Create and add this code to Order.cs

using System.ComponentModel.DataAnnotations;
using System.Web;
using Microsoft.Web.Helpers; // Install Microsoft.web.Helpers using Nuget

namespace Orders.Models
{
   
public class Order
   
{
       
public Order() { }

       
public Order(string text, string client)
       
{
           
Text = text;
           
Client = client;
       
}

       
public int ID { get; set; }

       
[Required]
       
public string Text { get; set; }

       
[Required]
       
[StringLength(10, ErrorMessage = "Client Name is too long! This was a Server validation.")]
       
public string Client { get; set; }

       
[Required]
       
public string Email { get; set; }

   
}

 
}

Now Lets Mock up some data for the Client.. So lets go ahead and create Interface IOrderRepository.

Create and add this code to IOrderRepository.cs under Models Folder

using System.Collections.Generic;

namespace Orders.Models
{
   
public interface IOrderRepository
   
{
       
IEnumerable Get();      
       
bool TryGet(int id, out Order Order);
       
Order Add(Order Order);
       
bool Delete(int id);
       
bool Update(Order Order);
   
}
}

Now Lets create a Order repository Dictionary to get , update , remove data from the repository

Create and add this code to DictionaryOrderRepository.cs under Models Folder

using System.Collections.Generic;
using System.Linq;

namespace Orders.Models
{
   
public class DictionaryOrderRepository : IOrderRepository
   
{
       
int nextID = 0;
       
Dictionary Orders = new Dictionary();

       
public IEnumerable Get()
       
{
           
return Orders.Values.OrderBy(Order => Order.ID);
       
}



       
public bool TryGet(int id, out Order Order)
       
{
           
return Orders.TryGetValue(id, out Order);
       
}

       
public Order Add(Order Order)
       
{
           
Order.ID = nextID++;
           
Orders[Order.ID] = Order;
           
return Order;
       
}

       
public bool Delete(int id)
       
{
           
return Orders.Remove(id);
       
}

       
public bool Update(Order Order)
       
{
           
bool update = Orders.ContainsKey(Order.ID);
           
Orders[Order.ID] = Order;
           
return update;
       
}
   
}
}

Then Create Some Initial data to the Order Repository

Create and add this code to your InitialData.cs under Models folder

namespace Orders.Models
{
   
public class InitialData : DictionaryOrderRepository
   
{
       
public InitialData()
       
{
           
Add(new Order
           
{
                ID
= 1,
               
Text = @"Order for 1000 Products",
               
Client = "Client1",
               
Email = "Client1@Client.com",
           
});
           
Add(new Order
           
{
                ID
= 1,
               
Text = "Order for 1 million Product2",
               
Client = "Client2",
               
Email = "Client2@Client.com"
           
});
           
Add(new Order
           
{
                ID
= 2,
               
Text = " 1 million Order for Product3",
               
Client = "Client3",
               
Email = "Client3@Client.com"
           
});
           
Add(new Order
           
{
                ID
= 3,
               
Text = "100 Order for Product4",
               
Client = "Client4",
               
Email = "Client4@Client.com"
           
});
       
}
   
}
}

Now Lets Create a new controller named OrdersController which has the GET, POST,PUT and DELETE methods.

Remember that this controller should inherit from ApiController

Add this code to OrdersController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using Orders.Models;
using System.Web.Http;

namespace Orders.Controllers
{

   
public class OrdersController : ApiController
   
{
       
IOrderRepository repository;

       
public OrdersController(IOrderRepository repository)
       
{
           
this.repository = repository;
       
}

       
#region GET

       
public IQueryable GetOrders()
       
{
           
return repository.Get().AsQueryable();
       
}


       
#endregion

       
#region POST

       
public HttpResponseMessage PostOrder(Order Order)
       
{
           
Order = repository.Add(Order);
           
var response = new HttpResponseMessage(Order, HttpStatusCode.Created);
            response
.Headers.Location = new Uri(Request.RequestUri, "/api/Orders/" + Order.ID.ToString());
           
return response;
       
}
       
#endregion

       
#region DELETE

       
public Order DeleteOrder(int id)
       
{
           
Order Order;
           
if (!repository.TryGet(id, out Order))
               
throw new HttpResponseException(HttpStatusCode.NotFound);
            repository
.Delete(id);
           
return Order;
       
}
       
#endregion


   
}
}

Okay Now lets configure the Global.asax

To mock up the data lets use Ninject. You can install Ninject using Nuget package

Add the following code in global.asax and your global.asax.cs should look like

using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using Orders.Filters;
using Orders.Models;
using Ninject;

namespace Orders
{
   
// Note: For instructions on enabling IIS6 or IIS7 classic mode,


   
public class MvcApplication : System.Web.HttpApplication
   
{
       
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
       
{
            filters
.Add(new HandleErrorAttribute());
       
}

       
public static void Configure(HttpConfiguration config)
       
{
            config
.Filters.Add(new ValidationActionFilter());

           
var kernel = new StandardKernel();
            kernel
.Bind().ToConstant(new InitialData());
            config
.ServiceResolver.SetResolver(
                t
=> kernel.TryGet(t),
                t
=> kernel.GetAll(t));
       
}

       
public static void RegisterRoutes(RouteCollection routes)
       
{
            routes
.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes
.MapHttpRoute(
               
"DefaultApi",
               
"api/{controller}/{id}",
               
new { id = RouteParameter.Optional });

            routes
.MapRoute(
               
"Default", // Route name
               
"{controller}/{action}/{id}", // URL with parameters
               
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
           
);

       
}

       
protected void Application_Start()
       
{
           
AreaRegistration.RegisterAllAreas();

           
RegisterGlobalFilters(GlobalFilters.Filters);
           
Configure(GlobalConfiguration.Configuration);
           
RegisterRoutes(RouteTable.Routes);

           
BundleTable.Bundles.RegisterTemplateBundles();
       
}
   
}
}

Oh wait.. We forgot to add Validation Filters.. Lets add it under Filters folder after creating it..
Create and add the code to ValidationActionFilter.cs under Filters folder

using System.Json;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;


namespace Orders.Filters
{
   
public class ValidationActionFilter : ActionFilterAttribute
   
{
       
public override void OnActionExecuting(HttpActionContext context)
       
{
           
var modelState = context.ModelState;
           
if (!modelState.IsValid)
           
{
                dynamic errors
= new JsonObject();
               
foreach (var key in modelState.Keys)
               
{
                   
var state = modelState[key];
                   
if (state.Errors.Any())
                   
{
                        errors
[key] = state.Errors.First().ErrorMessage;
                   
}
               
}

                context
.Response = new HttpResponseMessage(errors, HttpStatusCode.BadRequest);
           
}
       
}
   
}
}

Thats it.. Now lets create a client to consume the API.

API CLIENT

There are many ways of accessing from client.. like
Calling the API from your C# code or from Jquery by using or without using Knockout.js

Lets create a console application to consume the created API.

Dont forget to add references to the dll's . Install the required Nuget packages

using System.Net;
using System.Net.Http;
using System.Json;

Add this code to your Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Http;
using System.Json;

namespace ConsoleApplicationforTest
{
   
class Program
   
{
       
protected static readonly string _endpoint = "http:/localhost:Webapiportnumber/api/Orders";

       
static void Main(string[] args)
       
{
           
using (WebClient proxy = new WebClient()) // Using Web Client
           
{
               
var response = proxy.DownloadString(_endpoint);
               
Console.WriteLine(response);
               
Console.WriteLine("Press any key to get data using HTTP Client");
               
Console.ReadLine();
                httpclientdemo
();

           
}
       
}

       
public static void httpclientdemo() // using HTTP Client
       
{
           
HttpClient proxy = new HttpClient();
            proxy
.GetAsync(_endpoint).ContinueWith((r) =>
           
{
               
HttpResponseMessage response = r.Result;
                response
.Content.ReadAsAsync().ContinueWith(
               
(a) =>
               
{
                   
foreach (var w in a.Result)
                   
{
                       
Console.WriteLine(w.ValueOrDefault("ID").ToString());
                       
Console.WriteLine(w.ValueOrDefault("Text").ToString());
                       
Console.WriteLine(w.ValueOrDefault("Client").ToString());
                   
}
               
});

           
});

           
Console.ReadKey(true);

       
}
   
}
}

Now run both the applications.. Make sure you have the correct port number of the Webapi is specified as the endpoint address in the console application

Now you will be getting the Json output in the console window.
alt text

Using Knockout.js

Lets create a view or Html page for displaying the results Lets say APIClient.cshtml

add this code to it .I am sorry for the unclean mark ups.. I need to work on my text editor..

h2>Get/h2>
  button id
="getOrders">Get all orders/button>
  div
>
  ul data
-bind="template: {name: 'OrderTemplate', foreach: Orders}">
       
/ul>

        script id="OrderTemplate" type="text/
html">
            li class="
Order">
                header>
                    div class="
info">

                        strong>span data-bind="
text: Client">/span>/strong>
                    /div>
                    div class="
actions">
                        a class="
delete" data-bind="attr: { 'data-Order-id': ID }" href="#">Delete Order Id: /a>
                   
/div>
               
/header>
                div class="body">
                    p data-bind="text: Text">

                /
div>
           
/li>
        /
script>

        script type
="text/javascript">
            viewModel
= {
               
Orders: ko.observableArray([])
           
};

            ko
.applyBindings(viewModel);
       
/script>
 
/div>


          h2 >Add a  row/
h2>
    button id
="addorders">Make an order/button>

    div style
="display:none"  id="divshowadd">
     form id
="newOrderForm">
        fieldset
>
                legend
>New Order/legend>
                label
for="text">Order desc/label>
                textarea id
="text" name="text" data-val="true" data-val-required="A Descreption for Order is required.">/textarea>
                span class="field-validation-valid" data-valmsg-for="text" data-valmsg-replace="true">/
span>

                label
for="client">Client name
                input id
="client" name="author" type="text" data-val="true" data-val-required="Please enter Client name" value="" />
                span
class="field-validation-valid" data-valmsg-for="client" data-valmsg-replace="true">/span>

                label for="email">Email
                input id="email" name="email" type="text" data-val="true" data-val-required="What's your email address?" value="" /
>
                span
class="field-validation-valid" data-valmsg-for="email" data-valmsg-replace="true">/span>
                button type="submit">Submit
            /
fieldset>
       
/form>
    /
div>

And here is our jQuery code to populate the data.. I am putting this into get.js and adding the script reference to the page

$(function () {
    $
("#getOrders").click(function () {
       
// We're using a Knockout model. This clears out the existing Orders.
        viewModel
.Orders([]);
        $
.ajax({ url: "/api/Orders",
            accepts
: "application/json",
            cache
: false,
            statusCode
: {
               
200: function (data) {
                    viewModel
.Orders(data);
               
},
               
401: function (jqXHR, textStatus, errorThrown) {
                   
self.location = '/Account/Login/';
               
}
           
}
       
});


   
});



   
// $("#getOrdersdel").click(function () { $("#divgetOrdersdel").toggle(); });

    $
("#addorders").click(function () { $("#divshowadd").toggle(); });

});


//Post

$
(function () {
   
var form = $('#newOrderForm');
    form
.submit(function () {
       
var form = $(this);
       
var Order = { ID: 0, Text: $('#text').val(), Author: $('#author').val(), Email: $('#email').val(), GravatarUrl: '' };
       
var json = JSON.stringify(Order);

        $
.ajax({
            url
: '/api/Orders',
            cache
: false,
            type
: 'POST',
            data
: json,
            contentType
: 'application/json; charset=utf-8',
            statusCode
: {
               
201 /*Created*/: function (data) {
                    viewModel
.Orders.push(data);
               
},
               
401: function (jqXHR, textStatus, errorThrown) {
                    alert
('You are not authourized to make an order. Please log in');
                    $
("a#loginLink").click();
               
}
           
}
       
});

       
return false;
   
});
});

//delete

$
(function () {
    $
("a.delete").live('click', function () {
       
var id = $(this).data('Order-id');

        $
.ajax({
            url
: "/api/Orders/" + id,
            type
: 'DELETE',
            cache
: false,
            statusCode
: {
               
200: function (data) {
                    viewModel
.Orders.remove(
                       
function (Order) {
                           
return Order.ID == data.ID;
                       
}
                   
);
               
},
               
401: function (jqXHR, textStatus, errorThrown) {
                    alert
('You are not authourized to delete an order. Please log in');
                    $
("a#loginLink").click();
               
}
           
}
       
});

       
return false;
   
});
});

Dont forgot to add script reference to your Layout.cshtml page..

Thats it.. Now you can see the list of orders , you can add an order, delete an order. Everything happens asynchronously with great user experience..
You can Decorate Using Authourize attribute for authourization which will redirect to login page based on the status code returned..

Api client using knockout.js

Download the source code from here..

Post Comment | flag

Attachments :

2201203170230010903.zip
Gokul A
Submitted on: Mar 17, 12 at 2:30AM

0 Comments

If you have any issues in using authorize attribute on WebApi controllers please refer this article http://www.askamoeba.com/Answer/145/ASP-NET-MVC-4-WebAPI-authorization-Authorize-attribute-not-working-

by : Gokul A on : Mar 22, 12 at 1:37AM

Thanks.. Great work!!

by : Bac Gs on : Oct 06, 13 at 9:28PM

Post your Comment