-
Notifications
You must be signed in to change notification settings - Fork 0
HowtoPart2
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.
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.
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:
At this point, the Todomvc app is working again using the new refactored code.
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:
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:
If I look at the response body, I get the object I returned:
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" });
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.