Orchard CMS and ASP.NET Web API

Orchard CMS and ASP.NET Web API

Awhile ago I mentioned how Orchard 1.6 will be running ASP.NET MVC 4, ASP.NET Web API, and NHibernate 3. I have already covered ASP.NET Web API in more detail in one of my ASP.NET MVC 4 TutorialsASP.NET MVC 4 Web API Routes and ApiController, but I thought I would at least give Web API a quick shoutout with Orchard CMS. As with all things Orchard, Web API works exactly as you would expect and can take advantage of the various services and extensions that come with Orchard.

ApiController

The heart of ASP.NET Web API is the ApiController. You will derive your custom controller classes from it and follow its conventions. Let's say we have a Contact Content Type and want to provide an API for getting all the contacts in Orchard. One way, of many, to pull this off is to create a ContactsController that derives from ApiController and respond to the REST requests. In this case, I created a simple ContactViewModel that gets returned by the service. For simplicity, I just had the ContactViewModel take IContentItem as a parameter and do the datamapping, but you could certainly provide a datamapper, which would be more inline with best practices. The code looks like this.

public class ContactsController : ApiController {
    private readonly IContentManager _contentManager;

    public ContactsController(IContentManager contentManager) {
        _contentManager = contentManager;
    }

    public IEnumerable<ContactViewModel> Get() {
        return _contentManager
            .Query(VersionOptions.Published, "Contact")
            .List()
            .Select(c => new ContactViewModel(c));
    }
}

I created a module, called Sunkist.ContactManager, to hold my controller, viewmodel and datamigration file that creates the Contact Content Type, etc. Once I enable the module and enter a few contacts in the backend of Orchard I can request all the contacts using the default path.

http://.../api/sunkist.contactmanager/contacts

Web API does content negotiation and by default it will return XML when requested by the browser. I added some code (QueryStringMapping) to provide me JSON when I add ?format=json to the URL in the browser which gives me some test data I populated in Orchard. Obviously you can use various browser plugins that change the headers, etc. without the trick.

[{"Id":15,"Name":"John Doe","Address":"1234 Breeze Street",...},
{"Id":16,"Name":"Jane Doe","Address":"7483 Beach Chair Rd",...},
{"Id":17,"Name":"Tim Doe","Address":"2332 Water Bridge Ct.",...}]

If you just want to access the data for one contact, we can create a quick method that responds to the appropriate GET Request with the Contact's Id.

public ContactViewModel Get(int id) {
    var contact = _contentManager.Get(id);

    if (contact == null)
      throw new HttpResponseException
        (new HttpResponseMessage(HttpStatusCode.NotFound));

    return new ContactViewModel(contact);
}

You can access this via the following path and get the following results in JSON.

http://.../api/sunkist.contactmanager/contacts/15
{
    "Id":15,
    "Name":"John Doe",
    "Address":"1234 Breeze Street",
    "City":"Sarasota",
    "State":"Florida",
    "Zip":"34234",
    "Twitter":"@RockinJohnDoe"
}

You get the idea. This is normal ASP.NET Web API using Orchard Services.

Custom Routes

I don't particularly like the name of the Orchard Module, Sunkist.ContactManager, in the URL, so let's create a custom route for accessing a particular contact.

public class HttpRoutes : IHttpRouteProvider {

    public void GetRoutes(ICollection<RouteDescriptor> routes) {
        foreach (RouteDescriptor routeDescriptor in GetRoutes()) {
            routes.Add(routeDescriptor);
        }
    }

    public IEnumerable<RouteDescriptor> GetRoutes() {
        return new[] {
            new HttpRouteDescriptor {
                Name = "ContactsGet",
                Priority = -10,
                RouteTemplate = "api/contacts/{id}",
                Defaults = new {
                    area = "Sunkist.ContactManager",
                    controller = "Contacts",
                    action = "Get"
                },
            }
        };
    }
}

Now I can access the contact without the need for the name of the module in the route.

http://.../api/contacts/15

Conclusion

That's ASP.NET Web API with Orchard CMS at a very low level. Given Orchard has a lot of conventions this can obviously be made much easier for developers to provide plug-n-play REST functionality without writing all this code. Certainly a REST Module can be created that builds in a lot of this stuff for free with some practical extensions if you want something overridden. First things first, however. Now that we have ASP.NET MVC 4 and ASP.NET Web API built into Orchard 1.6 we can start taking this further.

You may want to check out my ASP.NET MVC 4 Tutorials for more information on ASP.NET Web API and other new features.

Enjoy!