Friday, January 20, 2012

Building a REST service in MVC .NET

This article got me started on the right track to implement a REST service in MVC:
http://iwantmymvc.com/rest-service-mvc3


One thing i think could be done more straightforward than in the examples is the switching on Http method. Instead of creating a filter to add a parameter to the method call i use the HttpMethod property of the Request object wich is available in you controller.

Code Snippet
  1. [Authorize]
  2. [AcceptVerbs(HttpVerbs.Delete|HttpVerbs.Get)]
  3. public ActionResult activities(Guid id) {
  4.     switch (Request.HttpMethod)
  5.     {
  6.         case "GET":
  7.             return Json(new ActivityViewModel());
  8.         case "DELETE":
  9.             return Json(true);
  10.     }
  11.     return Json(new { Error = true, Message = "Unsupported HTTP verb" });
  12. }

2 comments:

  1. The writer of the referenced article, Justin Schwartzenberger, has replied to my suggestion and rightfully suggested that the abstraction of the HTTP method outside of the controller would offer more possibilities for independent (unit) testing of the controller layer.
    Because of the relative simplicity of my project my solution still suffices but it would definitely something to consider in the future when the project grows.

    ReplyDelete
  2. you should probably use this instead of AcceptVerbs, which I completely copied from the Microsoft AcceptVerbs implementation and just turned it into a filter.

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public class AcceptVerbsActionFilterAttribute : ActionFilterAttribute
    {
    public AcceptVerbsActionFilterAttribute(HttpVerbs verbs)
    : this(EnumToArray(verbs))
    {
    }

    public AcceptVerbsActionFilterAttribute(params string[] verbs)
    {
    if (verbs == null || verbs.Length == 0)
    {
    throw new ArgumentException("Null or Empty", "verbs");
    }

    Verbs = new ReadOnlyCollection(verbs);
    }

    public ICollection Verbs
    {
    get;
    private set;
    }

    private static void AddEntryToList(HttpVerbs verbs, HttpVerbs match, List verbList, string entryText)
    {
    if ((verbs & match) != 0)
    {
    verbList.Add(entryText);
    }
    }

    internal static string[] EnumToArray(HttpVerbs verbs)
    {
    var verbList = new List();

    AddEntryToList(verbs, HttpVerbs.Get, verbList, "GET");
    AddEntryToList(verbs, HttpVerbs.Post, verbList, "POST");
    AddEntryToList(verbs, HttpVerbs.Put, verbList, "PUT");
    AddEntryToList(verbs, HttpVerbs.Delete, verbList, "DELETE");
    AddEntryToList(verbs, HttpVerbs.Head, verbList, "HEAD");

    return verbList.ToArray();
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
    if (filterContext == null)
    {
    throw new ArgumentNullException("filterContext");
    }
    var incomingVerb = filterContext.HttpContext.Request.HttpMethod;
    var contains = Verbs.Contains(incomingVerb.ToUpper());
    if (contains == false)
    {
    filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.MethodNotAllowed);
    }

    base.OnActionExecuting(filterContext);
    }

    }

    ReplyDelete