ASP.NET MVC3 RESTful application tutorial with Backbone.js – Part III

Took a while for our mix to stand but now it is time to go back to work on it! We still look forward to sample the Backbone Todo demo with an ASP.NET REST aftertaste. As you can see in title this is the third part of our tutorial. In the last two posts, we already added in the mixing bowl: c# and asp.net mvc3; today it’ll be the proper time to add a bit of REST here and there. If you are keen on doing the recipe yourself, you can start with the first part here: ASP.NET MVC3 Tutorial with Backbone.js – Part I. Otherwise if you are in a hurry you can start with our prepackaged mix of the first two parts: ASP.NET MVC3 Tutorial with Backbone.js – Part II.

Last time we dealt with Models and ViewModels, today we will speak about the core on the application: the REST Controller!

Controller

First of all, get rid of the About Controller: we won’t need it. We will work on the sample Home controller. In this section you will find the shortest implementation possible of the 4 REST verbs using JSON as transfer media, later we will adjust routing to fit in the new architecture.

Open your “HomeController” in the “Controller” folder. Trash everything: don’t worry we will replace the code straight after.

The following snippet is the REST controller of our application.

using System.Collections.Generic;
using System.Web.Mvc;
using Todo.DAL;
using Todo.ViewModels;

namespace Todo.Controllers
{
    public class HomeController : Controller
    {
        private IRepository<Todo> repository = null;

        public HomeController()
        {
            repository = new TodoRepository();
        }

        public HomeController(IRepository<Todo> repository)
        {
            this.repository = repository;
        }

        [HttpGet]
        public ActionResult Get()
        {
            var todos = repository.FindAll();
            var list = new List<TodoViewModel>();
            foreach (Todo t in todos)
                list.Add(new TodoViewModel(t));

            return Json(list, JsonRequestBehavior.AllowGet);
        }

        [HttpPost]
        public ActionResult Post(TodoViewModel newTodo)
        {
            Todo todo = new Todo();
            UpdateModel(todo, new[] { "Text", "Done", "Order" });
            ValidateModel(todo);
            repository.Add(todo);
            repository.Save();
            return Json(new TodoViewModel(todo));
        }

        [HttpPut]
        public ActionResult Put(int id, TodoViewModel newTodo)
        {
            Todo todo = repository.Get(id);
            UpdateModel(todo, new[] { "Text", "Done", "Order" });
            ValidateModel(todo);
            repository.Save();
            return Json(new TodoViewModel(todo));
        }

        [HttpDelete]
        public ActionResult Delete(int id)
        {
            Todo todo = repository.Get(id);
            if (todo != null)
            {
                repository.Delete(todo);
                repository.Save();
            }
            return Json(new { });
        }

        public ActionResult Index()
        {
            var todos = repository.FindAll();

            var list = new List<TodoViewModel>();
            foreach (Todo t in todos)
                list.Add(new TodoViewModel(t));

            return View(list);
        }
    }
}

You may recognize at this point the familiar Index action and the four REST actions: GET, POST, PUT and DELETE. As you may notice we take advantage of the Repository infrastructure and ViewModels coded last time and, as said, we return JSON strings to the invoking client. We are quite proud of the brevity of the code: that’s really pretty much everything you need. You could probably write more code to register the routes than to handle the controller actions themselves!

Routes

Now we just have to setup the routes to handle invocation. Our URLs will start in the format “/api/Todos” like:

  • /api/Todos for GET (all)
  • /api/Todos/{id} for GET (one)
  • /api/Todos for POST
  • /api/Todos/{id} for PUT
  • /api/Todos/{id} for DELETE

Open your “Global.asax”. We will rewrite the “RegisterRoutes” method. Here’s the code:

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

            var url = "api/Todos";

            routes.MapRoute(
                "Todo_GET",
                url,
                new { controller = "Home", action = "Get", area = "" },
                new { httpMethod = new HttpMethodConstraint("GET") }
            );

            routes.MapRoute(
                "Todo_GET_ONE",
                url + "/{id}",
                new { controller = "Home", action = "GetOne" },
                new { httpMethod = new HttpMethodConstraint("GET") }
            );

            routes.MapRoute(
                "Todo_POST",
                url,
                new { controller = "Home", action = "Post" },
                new { httpMethod = new HttpMethodConstraint("POST") }
            );

            routes.MapRoute(
                "Todo_PUT",
                url + "/{id}",
                new { controller = "Home", action = "Put" },
                new { httpMethod = new HttpMethodConstraint("PUT") }
            );

            routes.MapRoute(
                "Todo_DELETE",
                url + "/{id}",
                new { controller = "Home", action = "Delete" },
                new { httpMethod = new HttpMethodConstraint("DELETE") }
            );

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

        }

…and today’s work is done! You now have a working REST application. Next time we will garnish it adapting Backbone demo to our backend.

See you soon!

Tags: , , , , , ,

{ 8 comments to read ... please submit one more! }

  1. Great tutorial.
    Nice seeing some tutorials about RESTful applications with C# and .net, not only ruby and node.js.
    When is the next one coming?

  2. Thank you kurt!

    I’m nearly done with it..just a couple of things to polish ;)

    Stay tuned…!

  3. First of all, thank you for making this tutorial! I’ve been writing a Backbone.js application for a couple of months now. I think I kinda got the gist of it, but I’ve been pretty bummed that I didn’t really know how to use the methods to persist to the server in a way that Backbone expects! (That’s ok, I had some rather stringent requirements which I’m not sure would have worked in an ASP.NET MVC way… or at least any way I could have conceive of it working with my ignorance of the subject). This tutorial is really helping me understand how it all can work out! However, I do want to point out, you seem to be missing an Action for the “Todo_GET_ONE” route. I discovered it when trying to implement what you have here against my own model.

    This is what I have right now (translated to be inline with your example).

    [HttpGet]
    public ActionResult GetOne(int id)
    {
    var todo = repository.Get(id);
    var list = new List();
    if (todo != null)
    {
    list.Add(new TodoViewModel(todo));
    }
    return Json(list, JsonRequestBehavior.AllowGet);
    }

    I tried this (again, translated):

    [HttpGet]
    public ActionResult GetOne(int id)
    {
    var todo = repository.Get(id);
    TodoViewModel result = null;
    if (todo != null)
    {
    result = new TodoViewModel(todo);
    }
    return Json(result, JsonRequestBehavior.AllowGet);
    }

    But it doesn’t render *anything* when result is null, which is a bit surprising, because I don’t think a literal *empty* string is valid json :-D. It would be nice if I could coax it to return “null” (without the quotes) but as this is the first time I’ve tried ASP.NET MVC, don’t know that’s going to happen soon enough for my deadlines :-D. It might be better as a list with zero or one items anyway.

    I’m curious what your input is on the subject. Thanks again for the article!

  4. Hi JayC!

    1) About the GET_ONE route
    Yes, you are right: I wrote a route leading to nothing….! But I would like to point out that there’s no need for the matching action for the Todo demo.
    Of course, in a standard application, you may need a route for a GET_ONE item and your implementation is pretty much correct.

    2) Dealing with errors while fetching items
    If you want to display errors to your users, you can return a meaningful error code/state with or without a 500 http error code. Otherwise, if you not interested, in my opinion you can just return an empty list.

    Thanks for dropping by ;)
    Cheers

{ 2 Pingbacks/Trackbacks }

  1. Building a demo chat app in MVC3, backbone.js and a little SignalR for fun… | Rick Schott :: devlpr.net
  1. What happened this weekend… Why more app hacking of course… « Synthesis – The combination of ideas into a complex whole

Leave a Reply

Your email address will not be published. Required fields are marked *

*


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>