Creating RESTful .NET Services via ASP.NET MVC

A step-by-step guidance

While turning attention to REST, the ASP.NET MVC programming model seems to be best suited for creating such Services. At least at a first glance. This is due to the reason, the major architectural style and the paradigms, which both REST and MVC share, are the same. The talk is about the particular style resources are addressed via URIs. Unfortunately and despite these obvious affinities, ASP.NET MVC is not the first choice while looping through the embarrassingly rich list of possibilities and templates serving to create a RESTful Services.

In this article I will guide you through the steps in case you decide to start creating your RESTful .NET Services via the ASP.NET MVC Stack. I agree with you while claiming, that there are already published examples on the net. Why needlessly add another more one? The truth is however, that the examples I have found were either too complex or outdated. Take for example the great book written by Kenn Scribner and Scott Seely (Effective REST Services via .NET). The code to the book which you can download from this location, contains also an advanced example for a REST Service built via ASP.NET MVC. To be honest however I have poked around half a day only to get that example running. You have to create the database, configure it and insert the starting set of data and so on. Some error remediation hints you could find on the book’s download page, however even if you succeed and the example runs, it remains up to you figuring what exactly is going on there.

Another good example is Keith Brown’s msdn article titled Restful Services With ASP.NET MVC. This article is great however it focuses rather on issues around XHTML and will not teach you the basics you would desperately need to be familiar with the core principles while taking the ASP.NET MVC stack and turning it to a RESTful Service.

Further promising examples were built with earlier CTPs or Betas, and they simply didn’t work with the release Version of the current mainstream Framework.

So my intention is to take your hands and give you the cakes. In this article you will learn the basic principles developing RESTful services via the ASP.NET MVC programming model. You will need Visual Studio 2008 and ASP.NET MVC 1.0 installed and of course some basic understanding of the ASP.NET MVC and REST principles.

Prior to dive in to the example let me first list the major options enthusiastic people will face, while looking for common ways creating RESTful .NET Services.

  1. The first choice is always the out-of-the-box available WCF Stack within VS 2008. This means the Templates named like WCF Service Library or Web WCF Service Application.
  2. Next you could get started with ADO.NET Data Services. This could be an interesting option if your Data.Model is based on the Entity Framework and the service your are trying to build is rather read-only. The issue is, that while building full CRUD (Create, Read, Update, Delete) RESTful Service via ADO.NET Data Service, you will be forced to implement the IUpdateable interface, which is rather tedious.
  3. The third option is to download the REST Starter Kit Preview 2 from Codeplex. Installing the Kit you will have five more Visual Studio Templates available. Those Templates represent specialized and rather simple RESTful Services which are highly useful if your design plans are in accordance with those specialized services capabilities. Let me mention the Template for building RESTful Services based on the Atom Publishing Protocol.
  4. The fourth option represents the recent talk building RESTful Services using the ASP.NET MVC 1.0 Framework.
  5. The last option is based on another Codeplex download package, named the ASP.NET MVC REST SDK which is an effort to glue yet unfortunately separated parts together. I mean the RESTful Service idea with ASP.NET MVC as I mentioned at the beginning of this article. In this regard the SDK helps you develop an ASP.NET MVC based application, which will return to the requesting client XML or Json while asked nicely. This means using the Accept MIME-Type Header in the request. So the Service can decide whether to respond with a regular HTML View (user surfs the URI via a Web-Browser), or return XML (Accept:text/xml,application/xml), or just return Json (Accept:application/json).

The example I will guide you through is based on a simple custom Type User. Our attempt is to build a RESTful Service which will accept and return such User Type or Users Collection Type while the Service is build upon the ASP.NET MVC Framework.

namespace SharedUserLib
{
    public enum MembershipStatus
    {
        premium = 10,
        bronze,
        silver,
        gold
    }

    public class Users : List<User>
    {
        public Users() { }
        public Users(List<User> userlist) : base(userlist) { }
    }

    public class User
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public MembershipStatus Status { get; set; }
        public int Id { get; set; }
        public Uri UserId { get; set; } 
        public DateTime LastModified { get; set; }
    }
}

Code 1

You could start with a simple Library project which contains only the Types depicted above (Code 1) within a single file let be Users.cs. Thereafter add a new Project to the Solution taking the ASP.NET MVC Web Application Template and name the Project RestMvcApp. In the next step you could choose to create Unit-tests as well, however this is not going to be the major topic right now.

Picture 1

We will host the MVC Project within the local IIS and rather abandon the by default available Web Development Server. This seems to be later a good idea and enables us to utilize IIS 7 integrated pipeline mode (see more a little further below). Right mouse button on the just created new project chose Properties and take the Web tab. Switch to “Use Local IIS Web server” and click on “Create Virtual Directory”. This will hopefully end with a success message displayed in a popup Dialog-Box.

Picture 2

Next open Site.Master which is located at Views/Shared and add a third ActionLink entry below the existing two entries “Home” and “About”. The third entry line will ad a new menu, clicking on which the Service will respond immediately returning all Users available in the internal list. The menucontainer div element within the Site.Master could look like this code-segment below depicts:

<div id="menucontainer"> 
    <ul id="menu"> 
        <li><%= Html.ActionLink("Home", "Index", "Home")%></li> 
        <li><%= Html.ActionLink("About", "About", "Home")%></li> 
        <li><%= Html.ActionLink("RestService", "ServiceRequest", "RestService")%></li> 
    </ul> 
</div>

Code 2

Please note, that the first string “RestService” represents the new menu which will appear while opening the default.aspx home. The “ServiceRequest” string is the pre-defined action-name intended to divert all incoming requests to the RestServiceController class. The Controller Class, which will receive such requests is represented by the third string “RestService”. That string according to the ASP.NET MVC syntax agreement refers to the RestServiceController. Starting now the application you could see a similar picture like in Picture 3. Please note the menu is not yet implemented.

Picture 3

The next step is about registering the route for our RESTful requests. This could be done within the global.asax, which for reason please open-up that file and insert a dedicated route mapping within the RegisterRoutes method (see Code 3). The first parameter of the MapRoute method “Rest” is only the name of the new route. The second string is the URI-Template containing the mapped parameters “action” and “id”. The default value of the “action” parameter equals “ServiceRequest”, whereas the “id” does not have any default value and is intended to refer to the User’s Id (see Code 1). Some valid URIs could in this regard look like:

  1. http://localhost/RestMvcApp/RestService/ServiceRequest/1
  2. http://localhost/RestMvcApp/RestService/ServiceRequest/2
  3. http://localhost/RestMvcApp/RestService/ServiceRequest/
  4. http://localhost/RestMvcApp/RestService/
public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        "Rest",                                              
        "RestService/{action}/{id}",                           
        new { controller = "RestService", action = "ServiceRequest", id = "" }  
    );

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

Code 3

It is important to note at this point, that the action named “ServiceRequest” (see Code 3) will not change while reading, inserting, updating or even deleting an addressed resource. What actually will change is the http Verb issued from the client. And this is exactly the way any RESTful service works utilizing the uniform interface consisting of verbs like GET, PUT, POST, DELETE, HEAD, OPTIONS and so on.

As the appropriate Route for incoming requests is already registered now, and the Controller which will handle all such incoming RESTful requests is named (see Code 2), the next logical step is just adding that Controller to the project. Right mouse button the Controllers folder chose Add/Controller which will display the Add Controller Dialog-Box (Picture 4). Name the Controller like depicted RestServiceController. The check box could be left unchecked as we are going to add the methods supporting CRUD manually .

Picture 4

The RestServiceConroller class has by default a single Index method implemented, which returns a View, meaning some HTML-Content. The Index is the Default route’s default action value registered in the previously discussed global.asax file. As our new RestServiceController in this version is not intended to serve any HTML, we could safely delete the Index method as due to the registered routes the method would be anyway never called.

Furthermore please note, any Controller has to return an instance of the ActionResult class. The Controller’s View method returns a ViewResult which is derived from ViewResultBase which is at the end of the day derived from ActionResult. So whatever methods we are going to implement here in the RestServiceController Class, it has to return an ActionResult which is the way ASP.NET MVC likes it. In order to proceed let’s implement the four public methods’ skeletons serving the GET, PUT, POST and DELETE Verbs. Please note, that all Methods have the ActionNameAttribute set to “ServiceRequest” (according to the route in global.asax).

    public class RestServiceController : Controller
    {
        [AcceptVerbs("GET")]
        [ActionName("ServiceRequest")]
        public ActionResult GetRequest(string id)
        {
            
        }

        [AcceptVerbs("PUT")]
        [ActionName("ServiceRequest")]
        public ActionResult PutRequest(string id)
        {
            
        }

        [AcceptVerbs("POST")]
        [ActionName("ServiceRequest")]
        public ActionResult PostRequest()
        {
            
        }

        [AcceptVerbs("DELETE")]
        [ActionName("ServiceRequest")]
        public ActionResult DeleteRequest(string id)
        {
            
        }
    }

Code 4

Furthermore note, that only the GET Verb has to return some non null HTML-Body while the PUT, POST and DELETE Verbs are safe to return only an appropriate HTTP-Header containing valid HTTP Status codes. Here below the Table 1 summarizes the requirements on the Methods:

Verb

Response body

Method name

URI Template

Header: Status codes success/failure

GET one User GetRequest ServiceRequest/{id} 200 OK / 404 Not found
GET Users Collection GetRequest ServiceRequest/{id}
where the id is blank
200 OK
POST void PostRequest ServiceRequest 201 Created / 500 Server error
Location Header contains the created resource’s URI
PUT void PutRequest ServiceRequest/{id} 202 Accepted / 404 Not found
DELETE void DeleteRequest ServiceRequest/{id} 200 OK / 404 Not found

 Table 1

So in order to proceed let’s complete the methods returning new instances derived from the ActionResult Class. Let name those new Classes according to the affected Verbs, like GetServiceActionResult, PutServiceActionResult, PostServiceActionResult and DeleteServiceActionResult which completes the RestServiceController’s method like Code 5 depicts:

    public class RestServiceController : Controller
    {
        [AcceptVerbs("GET")]
        [ActionName("ServiceRequest")]
        public ActionResult GetRequest(string id)
        {
            return new GetServiceActionResult(id);
        }

        [AcceptVerbs("PUT")]
        [ActionName("ServiceRequest")]
        public ActionResult PutRequest(string id)
        {
            return new PutServiceActionResult(id); 
        }

        [AcceptVerbs("POST")]
        [ActionName("ServiceRequest")]
        public ActionResult PostRequest()
        {
            return new PostServiceActionResult();
        }

        [AcceptVerbs("DELETE")]
        [ActionName("ServiceRequest")]
        public ActionResult DeleteRequest(string id)
        {
            return new DeleteServiceActionResult(id);
        }
    }

Code 5

Next we are going to draw our attention to the Model. The four Classes above derived from ActionResult are parts of the Model. Additionally here belongs the Class responsible for all the Users created, deleted and updated. Name it UsersRepository. In order to keep the example as simple as possible, there are no databases involved.

Right mouse button on the Model folder and chose Add/New Class. Name the class like stated UsersRepository. The Class is a very simplified data-model containing a dictionary named Items for all User instances. In order to preserve the dictionary between web-requests, we are going to put a single instance of the UsersRepository Class into the ASP.NET Application instance. Note that the Constructor creates two new User instances for pure convenience purposes only (Code 6). In order to create some valid Uri for each User (see UserId), the Constructor takes a single string parameter, which contains the current Http-Endpoint of the Host.

And here how the Repository could look like. In order to avoid compiler errors please do not forget to add an assembly Reference to the SharedUserLibrary Project.

public class UsersRepository
{
  public Dictionary<string, User> Items { get; set; }
  public int InstanceCounter { get; set; }

  public UsersRepository(string domain)
  {
    Items = new Dictionary<string, User>();
    if (Items.Count == 0)
    {
        User user = new User();
        user.FirstName = "John";
        user.LastName = "Doue";
        user.Id = ++InstanceCounter;
        user.LastModified = DateTime.Now;
        user.Status = MembershipStatus.bronze;
        user.UserId = new Uri(String.Format("{0}{1}",
                 domain, user.Id.ToString()));

        Items.Add(user.Id.ToString(), user);

        user = new User();
        user.FirstName = "Johny";
        user.LastName = "Depp";
        user.Id = ++InstanceCounter;
        user.LastModified = DateTime.Now;
        user.Status = MembershipStatus.premium;
        user.UserId = new Uri(String.Format("{0}{1}",
                 domain, user.Id.ToString()));

        Items.Add(user.Id.ToString(), user);
    }
  }

  public static UsersRepository GetFromApplication(
            ControllerContext context)
  {
      return context.HttpContext.Application["Repository"] as UsersRepository;
  }

  public static void SetToApplication(
        ControllerContext context,
        UsersRepository repository)
  {
      context.HttpContext.Application["Repository"] = repository;
  }
}

Code 6

The single UsersRepository instance will be created while starting the Application, so the Application_Start Method within the global.asax has to be extended in that regard (see Code 7).

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);

    // Workaround
    string domain = "http://localhost/RestMvcApp&quot;;
    domain = String.Format("{0}{1}",
                domain,
                "/RestService/ServiceRequest/");

    UsersRepository repository = new UsersRepository(domain);
    Application["Repository"] = repository;
}

Code 7

Let’s proceed with the Model adding the four introduced earlier Classes which are instantiated and returned by the RestServiceController. These Classes serve these listed purposes:

  1. De-Serializing the request if any (POST and PUT)
  2. Serializing the response (GET only)
  3. Interacting with the Data-Model, which in this simplified case means interacting with UsersRepository instance (all Verbs)
  4. Setting the appropriate Status-Codes (all Verbs)
  5. Setting the appropriate Response MIME Type (GET only)

At this point add a new assembly Reference to the System.Runtime.Serialization Assembly. This is due to the reason we will deal with the DataContractSerializer and DataContractJsonSerializer Classes being part of that serialization Library. Thereafter right mouse button the Model folder and chose Add/New Class for each specialized Class (which are derived from ActionResult). See Code 8 for GetServiceActionResult and Code 9 for PostServiceActionResult Classes.

public class GetServiceActionResult : ActionResult
{
    protected string _id = "";

    public GetServiceActionResult(string id)
    {
        _id = id;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        RenderXml(context);
        //RenderJson(context)
    }

    private void RenderXml(ControllerContext context)
    {
        context.HttpContext.Response.ContentType = "text/xml";

        UsersRepository repository = UsersRepository.GetFromApplication(context);
        Dictionary<string, User> items = repository.Items;

        if (_id.Length == 0)
        {
            // return all
            var list = from x in items
                       select x.Value;

            Users users = new Users(list.ToList<User>());

            // Create a serializer
            DataContractSerializer serializer = new DataContractSerializer(typeof(Users));
            serializer.WriteObject(
        (Stream)context.HttpContext.Response.OutputStream, users.ToArray());
        }
        else if (items.ContainsKey(_id))
        {
            User user = items[_id] as User;
            DataContractSerializer serializer = new DataContractSerializer(typeof(User));
            serializer.WriteObject(
        (Stream)context.HttpContext.Response.OutputStream, user);
        }
        else
        {
            context.HttpContext.Response.StatusCode = (Int32)System.Net.HttpStatusCode.NotFound;
            // setting StatusDescription works only while using IIS integrated pipeline mode
            context.HttpContext.Response.StatusDescription = "The requested User does not exist";
        }
    }
}

Code 8

Note taking a focused look on the GetServiceActionResult Class, that it implements an overridden ExecuteResult method of the derived ActionResult Class. This method is the centerpiece in ASP.NET MVC while dealing with incoming requests and also while rendering responses. Note this method takes ControllerContext as a single parameter which provides access to HttpContext.

Also note, that requesting an Id which does not exists in the Items Dictionary, the method will only return a 404 – Not Found Status-Code. For convenience we could set a custom Status-Code containing the exact reason of the failure. While running however this piece in the Visual Studio provided Web Development Server (formerly Cassini), the StatusDescription property can’t be set and scrutinizing the HttpContext in the Debugger, you will see a hint along with an internal Exception stating that this assignment needs IIS integrated pipeline mode. So you remember, this is the reason we started the Project setup immediately in that way.

This particular implementation (Code 8) renders XML and accordingly sets the ContentType property to “text/xml”. It is easy to render Json formatted response. You would only need to replace the XML Serializer Class with DataContractJsonSerializer and also setting appropriately the ContentTpye to “application/json”.

The somewhat tougher question is, how to decide, what format exactly to return? I have seen some suggestions relying on the incoming request’s Accept Header, which contains the accepted by the client MIME-Types. The HttpContext.Request.AcceptTypes is the property which provides this piece of information. So if the client accepts text/xml, let return her text/xml while accepting only application/json let switch to the DataContractJsonSerializer. The only problem arises if the clients accepts “*/*” meaning everything or this header is not there at all. So we do have a problem not knowing what exactly to return. Probably the default should be each time XML. The REST Starter Kit Preview 2 implements Json Serialization only while explicitly asked via an additional parameter at the end of the URI “?format=json” in the URI-Request.

Let’s show the source code of one more class derived from ActionResult:

public class PostServiceActionResult : ActionResult
{
    public PostServiceActionResult()
    {

    }

    public override void ExecuteResult(ControllerContext context)
    {
        DataContractSerializer serializer = new DataContractSerializer(typeof(User));
        // Deserialize
        User user = serializer.ReadObject(
        context.HttpContext.Request.InputStream) as User;
        user.LastModified = DateTime.Now;
        
        Object syncObject = context.HttpContext.Application;

        Monitor.Enter(syncObject);
        UsersRepository repository = UsersRepository.GetFromApplication(context);
        user.Id = ++repository.InstanceCounter;
        user.UserId = new Uri(String.Format(
        "{0}{1}",
        context.HttpContext.Request.Url.ToString(), user.Id.ToString()));
        repository.Items.Add(user.Id.ToString(), user);
        UsersRepository.SetToApplication(context, repository); 
        Monitor.Exit(syncObject);

        // Indicate it has been created
        // This operation requires IIS integrated pipeline mode !!
        context.HttpContext.Response.Headers.Add(
            "Location",
            user.UserId.ToString());

        // Assign created return code
        context.HttpContext.Response.StatusCode = (Int32)HttpStatusCode.Created;
    }
}

Code 9

Note that Code 9 for POST implements also some primitive synchronization which in this particular situation is only needed for the singleton nature of the Dictionary and the UsersRepository Class instances. The completed Controllers and Model folder of the MVC Project should look in Solution explorer like Picture 5 depicts.

Picture 5

The complete source code download is here:

http://cid-8d365142bc4869ab.skydrive.live.com/embedicon.aspx/.Documents/RestMvcApp.zip

 

Conclusion: The example focuses on the ASP.NET MVC model while creating a RESTful .NET Service. The example is kept as simple as possible in order to understand the centerpiece of such a Task, which could be summarized as follows:

  1. Create a dedicated Controller which will serve the REST requests
  2. Register the Route and divert all REST requests to that created Controller
  3. Serialize and De-Serialize the requests within the Controller’s methods appropriately
  4. Set valid Status-Codes within the Controller’s methods
  5. Decide between XML or Json serialization
Advertisements
This entry was posted in Computer and Internet. Bookmark the permalink.

8 Responses to Creating RESTful .NET Services via ASP.NET MVC

  1. ez says:

    Hey Stefan, great job on this article! (i know it’s been ages since you wrote this article..) One of the issues lacking on the web are examples of consuming RESTful services using MVC.Net – could you please refer me in the right direction?

    • Stefan R. says:

      The problem around pure RESTful Services is that there is no convenience API or methodology to consume them. This means, while consuming SOAP services you could ask for the WSDL and you could therefore work fine with the supported types on the client. While talking about REST, there is no such thing like WSDL so the client has to issue pure HTTP-Requests using .NET Networking Programming. Another alternative is to consume such services via jQuery.ajax(). Microsoft and others have recognized the client side programming pain and have created a hybrid solution named OData = Open Data Protocol. This uses REST in the core/fabric; however the types are described using the ATOM Publishing Protocol. To keep this response short, let’s summarize: the preferred way to write RESTFul services is to do this based upon OData. OData is REST however not every REST Service is OData compliant. While consuming OData Services, you will get a proxy (simply add service reference in Visual Studio) and you could use even LINQ-Style programming to access the Resources behind OData/REST Services.

  2. It’s great that you are getting thoughts from this article as well as from our discussion made at this time.

  3. Hello friends, nice paragraph and nice arguments
    commented at this place, I am truly enjoying by these.

  4. What i do not realize is actually how you are not
    really much more well-appreciated than you may be right now.

    You’re so intelligent. You recognize therefore considerably with regards to
    this topic, made me personally believe it from so many various
    angles. Its like women and men are not fascinated unless it’s one thing to do with Lady gaga!
    Your individual stuffs great. All the time maintain it up!

  5. Lourdes says:

    I аm genuinely thankful to the owner of this web site who has shared this wonderful paragraph at
    here.

  6. Hi there, I сheck your new stuff on a regular basis. Your story-telling style is awesοme, keep dоing what yοu’re doing!

  7. Simply created a Twitter account to obtain some information on the
    move, certainly going to follow this blog though as you have some actually good posts

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s