Skip to content
This repository has been archived by the owner on Jul 27, 2022. It is now read-only.

HowtoPart2

gregmac edited this page Feb 24, 2012 · 3 revisions

NServiceMVC Todomvc Example Part 2

In the first Howto, I walked through the basic setup of a REST service, and showed some of the basics of NServiceMVC.

In this demo, I'll go through some more features and do things in a slightly nicer way.

I'm doing this demo in the part2 branch.

Refactor to use IoC Container

I have very ugly non-threadsafe code in my controllers. The first thing I'm going to do is refactor that out so I have a storage provider for my Todos, and then register that provider with an IoC container (I'm using Autofac).

I'll write a quick in-memory list of Todos, which is basically just a thread-safe wrapper for a Generic.List<Todo> with some of the Todo-specific logic thrown in.

TodoStorage public method summary

Now I can update my controller code to take advantage of this:

    public class TodosController : ServiceController
    {
        protected TodoStorage Todos; 
        public TodosController(TodoStorage todos)
        {
            Todos = todos; // injected by IoC container
        }

        [GET("todos")]
        [Description("List all Todos")]
        public IEnumerable<Models.Todo> Index()
        {
            return Todos.GetAll();
        }

        [POST("todos")]
        [Description("Add a Todo")]
        public Models.Todo Add(Models.Todo item)
        {
            Todos.Add(item);
            return item;
        }

        [PUT("todos/{id}")]
        [Description("Update an existing Todo")]
        public object Update(Guid id, Models.Todo item)
        {
            Todos.Update(id, item);
            return null;
        }

        [DELETE("todos/{id}")]
        [Description("Delete a Todo")]
        public object Delete(Guid id)
        {
            Todos.Delete(id);
            return null;
        }
    }

After installing the "Autofac MVC3 integration" nuget package, I'll register my TodoStorage instance with Autofac in Global.asax.cs:

Set up autofac

At this point, the Todomvc app is working again using the new refactored code.

Handle Errors

So what happens right now if I try to do something invalid, like delete an ID that doesn't exist? I'll fire up the metadata page and find out:

Delete response Delete response headers

As you can see it returned NULL. Looking at the response headers, I can see it actually returned a 200 OK response. This isn't very friendly to a consumer of this service, because it's not what happened.

NServiceMVC handles errors using exceptions (you can read more about NServiceMVC error handling). So let's throw an exception if the item doesn't exist. Note that the TodoStorage.Remove() method returns a boolean: true if the item was deleted, false if the id doesn't exist. With that in mind, I'll rewrite my service method:

        [DELETE("todos/{id}")]
        [Description("Delete a Todo")]
        public object Delete(Guid id)
        {
            if (!Todos.Delete(id))
            {
                throw new ServiceException(new { Message = "Failed to find requested id to delete" });
            }
            return null;
        }

Now when I do the same request, I get a 500 server error response:

Delete invalid ID returns 500 error

If I look at the response body, I get the object I returned:

Delete invalid ID response body

Note in this case, I made an anonymous object, but this could be a specific type you use for errors, or really anything you want so long as it can be serialized. There are also overloaded ServiceException calls that let you return a specific HTTP status code instead of 500, so for example you could return a 404: throw new ServiceException(System.Net.HttpStatusCode.NotFound, new { Message = "Failed to find requested id to delete" });

More methods

Just to round out the API (even though Todomvc doesn't make use of any of this) here are some more methods as examples.

        [DELETE("todos")]
        [Description("Delete all Todos")]
        public object Delete()
        {
            Todos.DeleteAll();
            return null;
        }

        [GET("todos/count")]
        [Description("Get the count of Todos. Not really RESTful, but you have this flexibility.")]
        public int Count()
        {
            return Todos.Count();
        }

        [GET("todos/{id}")]
        [Description("Load a specific Todo")]
        public Models.Todo Load(Guid id)
        {
            var todo = Todos.GetById(id);
            if (todo == null)
                throw new ServiceException(System.Net.HttpStatusCode.NotFound, new { Message = "Failed to find specified id" });

            return todo;
        }

It's now possible to do a DELETE /todos to erase everything. You can also load individual Todo objects if you know their id.

Also, showing the flexibility of using MVC routes for building services, it's very easy to have todos/count return an integer count of the current number of Todos, even though todos/{anything else} will invoke our todos/{id} method.

Clone this wiki locally