ASP.NET MVC 3, with its glorious URL structures and ease of working with and controlling HTTP request/response data is primed to build REST type API services. But how does one accomplish that and what does the whole RESTful thing really mean?

Building a full blown API (of any type) involves a lot of architecture components, from data validation to security and beyond. This post does not attempt to address all of that. It focuses on the initial structure of a RESTful service within an ASP.NET MVC 3 application that works with JSON data in and out. We will look at how we can use the route engine, the HTTP verb attributes and a lean controller design to provide a starting point for a REST API.

We start by making use of Areas in MVC to create an API Area within an application. This will allow us to isolate the API and use another Area or even the top level to add documentation and other API support tools like a web interface for testing the API (outside the scope of this post).



Our sample API will handle Comment data. The class for a comment:

namespace Website.Areas.Api.Models
{
    public class Comment
    {
        public int Id { get; set; }
        public string Subject { get; set; }
        public string Body { get; set; }
        public string AuthorName { get; set; }
    }
}

The API will support sending and receiving JSON structured data. We will be able to send in a Comment object in JSON like so:

{
    "Subject": "A Subject",
    "Body": "The Body",
    "AuthorName": "Jervis"
}

or a batch of Comments:

{
    items: [{
        "Subject": "A Subject",
        "Body": "The Body",
        "AuthorName": "Jervis"
    }, {
        "Subject": "A Second Subject",
        "Body": "The Other Body",
        "AuthorName": "Kevin"
    }]
}

Returned JSON for a single Comment will look like:

{
    "Id": 3,
    "Subject": "A Subject",
    "Body": "The Body",
    "AuthorName": "Jervis"
}

A set of Comments returned like so:

[{

    "Id": 1,
    "Subject": "A Subject",
    "Body": "The Body",
    "AuthorName": "Jervis"
}, {
    "Id": 2,
    "Subject": "A Second Subject",
    "Body": "The Other Body",
    "AuthorName": "Kevin"
}]

A brief REST before we route and control

To craft a RESTful solution there are a couple of targets that we want to hit. The first is the use of HTTP verbs to handle relative actions.

- GET Used to request data
- POST Used to create a new data record or a set of new data records
- PUT Used to update an existing data record
- DELETE Used to delete an existing data record

The second is to use a url structure that embodies a human readable request for data.

- GET
/Api/Comments
/Api/Comments/2/10
/Api/Comments/Comment/3
-
POST
/Api/Comments/Comment
-
PUT
/Api/Comments/Comment/3
-
DELETE
/Api/Comments/Comment/3

Routing


Within the area registration code (ApiAreaRegistration.cs) we can add routes for our RESTful url patterns. Let’s take a look at the code for the routes and then go over their purpose.

using System.Web.Mvc;

namespace Website.Areas.Api
{
    public class ApiAreaRegistration : AreaRegistration
    {
        public override string AreaName { get { return "Api"; } }

        public override void RegisterArea(AreaRegistrationContext context)
        {
            context.MapRoute(
                "SingleComment",
                "Api/Comments/Comment/{id}",
                new { controller = "Comments", action = "Comment",
                    id = UrlParameter.Optional }
            );
            context.MapRoute(
                "ListComments",
                "Api/Comments/{page}/{count}",
                new { controller = "Comments", action = "CommentList",
                    page = UrlParameter.Optional, count = UrlParameter.Optional }
            );
            context.MapRoute(
                "ListCommentsAll",
                "Api/Comments",
                new { controller = "Comments", action = "CommentList",
                    page = UrlParameter.Optional, count = UrlParameter.Optional }
            );

            context.MapRoute(
                "Api_default",
                "Api/{controller}/{action}/{id}",
                new { action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}

Routes do not have any knowledge of the HTTP verbs, so the ones that we add are going to support multiple scenarios to get to our controller actions. The SingleComment route supports our GET, PUT and DELETE requests when an id is included in the url string, and the POST when the id is left off. The ListComments route supports a GET request for paging Comments in which a page number and a count per page is included in the url string. The ListCommentsAll route handles a GET request for all Comments (no paging). It will also support a POST request of a list of Comments where the url string does not include anything after the /Api/Comments.

Before we take a look at the
CommentsController
code, let’s check out a custom ActionFilterAttribute that we can craft to help us handle multiple verbs through a single controller action.

using System.Web.Mvc;

namespace Website.Models
{
    public class RestHttpVerbFilter : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var httpMethod = filterContext.HttpContext.Request.HttpMethod;
            filterContext.ActionParameters["httpVerb"] = httpMethod;
            base.OnActionExecuting(filterContext);
        }
    }
}

This code will capture the HTTP verb of the request and store it in the ActionParameters collection. By applying this attribute to a controller action we can add a method parameter named httpVerb and the RestHttpVerbFilter will handle binding the HTTP request verb value to it. Our controller needs to support an action method with a common signature (the same parameters) but take different actions based on the HTTP verb. It is not possible to override a method with the same parameter signature but a different HTTP verb attribute. This custom attribute will allow us to have a single controller action method that can take action based on the HTTP verb without having to contain the logic to determine the verb. With a single controller this is not that big of a deal, but I’d imagine that our API is going to embody more than just Comment management, and thus a need to repeat the verb capture code in multiple controllers for the API.

Let’s take a look at the
CommentsController
code and see how this all unfolds:

using System.Collections.Generic;
using System.Web.Mvc;
using Website.Areas.Api.Models;
using Website.Models;

namespace Website.Areas.Api.Controllers
{
    public class CommentsController : Controller
    {
        ICommentManager commentManager;

        public CommentsController()
        {
            this.commentManager = new CommentManager();
        }

        [HttpGet]
        public JsonResult CommentList(int? page, int? count)
        {
            var model = this.commentManager.GetComments(page, count);
            return Json(model, JsonRequestBehavior.AllowGet);
        }

        [HttpPost]
        public JsonResult CommentList(List<Comment> items)
        {
            var model = this.commentManager.CreateComments(items);
            return Json(model);
        }

        [RestHttpVerbFilter]
        public JsonResult Comment(int? id, Comment item, string httpVerb)
        {
            switch(httpVerb)
            {
                case "POST":
                    return Json(this.commentManager.Create(item));
                case "PUT":
                    return Json(this.commentManager.Update(item));
                case "GET":
                    return Json(this.commentManager.GetById(id.GetValueOrDefault()),
                        JsonRequestBehavior.AllowGet);
                case "DELETE":
                    return Json(this.commentManager.Delete(id.GetValueOrDefault()));
            }
            return Json(new { Error = true, Message = "Unknown HTTP verb" });
        }
    }
}

The controller has a private member of type ICommentManager and the controller constructor instantiates an object of type CommentManager
that will implement the interface. The contract for the interface looks like so:

using System.Collections.Generic;

namespace Website.Areas.Api.Models
{
    public interface ICommentManager
    {
        Comment Create(Comment item);
        List<int> CreateComments(List<Comment> items);
        Comment Update(Comment item);
        Comment GetById(int id);
        List<Comment> GetComments(int? page, int? count);
        bool Delete(int id);
    }
}

The first controller action, CommentList(int? page, int? count), supports the HTTP GET verb only and handles querying comments. The logic to determine if the list of comments is paged or not will be brokered off to the CommentManager. The returned List<Comment>
data is sent to the JsonResult which will handle serializing it to JSON and providing the correct response headers to the client consuming the API. This method is hit whenever a GET request is made with the following url string structures:

/Api/Comments
/Api/Comments/2/10

The next controller action, CommentList(List<Comment> items)
, supports the HTTP POST verb only and is used to support adding multiple Comment objects in a single request. This method is hit whenever a POST request is made with the following url string structure:

/Api/Comments

The final controller action, Comment(int? id, Comment item, string httpVerb)
, is where most of the magic happens. This method supports all four verbs when a request is made with the following url string structures:

/Api/Comments/Comment
/Api/Comments/Comment/3

The Comment method brokers all logic to work with a single comment to the appropriate CommentManager method and returns the result of those methods directly through the JsonResult
. If, for some insane reason (HTML 6 arrives with new verbs), we receive an unsupported HTTP verb we return a custom JSON error object.

Testing the API with Fiddler

We can make use of
Fiddler to test the API. First we need to create the CommentManager class and give it some sample logic to return some test data.

using System.Collections.Generic;

namespace Website.Areas.Api.Models
{
    public class CommentManager : ICommentManager
    {
        public Comment Create(Comment item)
        {
            item.Id = 1;
            return item;
        }

        public List<int> CreateComments(List<Comment> items)
        {
            return new List<int> { 1, 2, 3 };
        }

        public Comment Update(Comment item) { return item; }

        public Comment GetById(int id)
        {
            return new Comment
            {
                Id = id,
                Subject = "Loaded Subject",
                Body = "Loaded Body",
                AuthorName = "Loaded Author"
            };
        }

        public List<Comment> GetComments(int? page, int? count)
        {
            var comment1 = new Comment
            {
                Id = 1,
                Subject = "First Subject",
                Body = "First Body",
                AuthorName = "First Author"
            };
            var comment2 = new Comment
            {
                Id = 2,
                Subject = "Second Subject",
                Body = "Second Body",
                AuthorName = "Second Author"
            };
            var items = new List<Comment> { comment1, comment2 };
            return items;
        }

        public bool Delete(int id) { return true; }
    }
}

Then we can F5 the project to have the API running, copy the localhost with port url and use that in Fiddler to send in HTTP requests. The url for my instance:

http://localhost:24771/

Creating a new request in Fiddler is done by clicking on the Request Builder tab. Leaving the drop down for the verb to GET and setting the url to http://localhost:24771/Api/Comments, we can execute it and see that application is hit (by the result in the Web Sessions panel on the left).



Selecting the session result, clicking on the Inspectors tab and then on the Raw view button allows us to view the response data.



The other GET requests are done the same, but with different urls. The POST, PUT and DELETE actions involve changing the HTTP verb drop down, using either the http://localhost:24771/Api/Comments url for a POST of multiple comments or the http://localhost:24771/Api/Comments/Comment/{id} url for working with a single comment. Within the Request Body text area we can add the JSON object that we want to send to the API. The only other piece that we need to handle is telling the API via the HTTP header that the content type is JSON. In the Request Headers text area we need to add the following line:

Content-Type: application/json

MVC will identify this header and use the JSON model binding to map the request body data to a Comment object in our controller actions (or the List<Comment> item
object if we are doing a POST of multiple Comments at once). Posting a new comment to the API with Fiddler looks like so:



If we set breakpoints throughout the
CommentsController
we can go through the various requests with Fiddler and validate that our routing is working as planned and that we are reaching the correct action methods for each type of request.

Can I hand out my API url now?

Remember, this is just a starting point for creating REST type functionality in an MVC 3 application. There is a long way to go to craft out a true API solution. The next step would be to fill out the logic for the
CommentManager to work with a data storage layer for persisting the Comment data. After that, adding some logic in the CommentsController.Comment
method to support clients that don’t support the PUT or DELETE verbs. From there the fun begins. Thinking about security, data validation, standard error response support, cross domain support, and on and on.

If you are in need of a way to write a RESTful service within MVC 3 hopefully this can help get you rolling.

Reasons why you must trust ASPHostPortal.com

Every provider will tell you how they treat their support, uptime, expertise, guarantees, etc., are. Take a close look. What they’re really offering you is nothing close to what ASPHostPortal does. You will be treated with respect and provided the courtesy and service you would expect from a world-class web hosting business.

You’ll have highly trained, skilled professional technical support people ready, willing, and wanting to help you 24 hours a day. Your web hosting account servers are monitored from three monitoring points, with two alert points, every minute, 24 hours a day, 7 days a week, 365 days a year. The followings are the list of other added- benefits you can find when hosting with us:

-
DELL Hardware
Dell hardware is engineered to keep critical enterprise applications running around the clock with clustered solutions fully tested and certified by Dell and other leading operating system and application providers.
- Recovery Systems
Recovery becomes easy and seamless with our fully managed backup services. We monitor your server to ensure your data is properly backed up and recoverable so when the time comes, you can easily repair or recover your data.
- Control Panel
We provide one of the most comprehensive customer control panels available. Providing maximum control and ease of use, our Control Panel serves as the central management point for your ASPHostPortal account. You’ll use a flexible, powerful hosting control panel that will give you direct control over your web hosting account. Our control panel and systems configuration is fully automated and this means your settings are configured automatically and instantly.
- Excellent Expertise in Technology
The reason we can provide you with a great amount of power, flexibility, and simplicity at such a discounted price is due to incredible efficiencies within our business. We have not just been providing hosting for many clients for years, we have also been researching, developing, and innovating every aspect of our operations, systems, procedures, strategy, management, and teams. Our operations are based on a continual improvement program where we review thousands of systems, operational and management metrics in real-time, to fine-tune every aspect of our operation and activities. We continually train and retrain all people in our teams. We provide all people in our teams with the time, space, and inspiration to research, understand, and explore the Internet in search of greater knowledge. We do this while providing you with the best hosting services for the lowest possible price.
- Data Center
ASPHostPortal modular Tier-3 data center was specifically designed to be a world-class web hosting facility totally dedicated to uncompromised performance and security
- Monitoring Services
From the moment your server is connected to our network it is monitored for connectivity, disk, memory and CPU utilization – as well as hardware failures. Our engineers are alerted to potential issues before they become critical.
- Network
ASPHostPortal has architected its network like no other hosting company. Every facet of our network infrastructure scales to gigabit speeds with no single point of failure.
- Security
Network security and the security of your server are ASPHostPortal’s top priorities. Our security team is constantly monitoring the entire network for unusual or suspicious behavior so that when it is detected we can address the issue before our network or your server is affected.
- Support Services
Engineers staff our data center 24 hours a day, 7 days a week, 365 days a year to manage the network infrastructure and oversee top-of-the-line servers that host our clients’ critical sites and services.