Not long ago I discovered OpenRasta. If you’re not familiar with it it’s described as a “resource-oriented framework for .NET enabling easy ReST-ful development of websites and services”.
After reading Rails 3 in a Nutshell I started looking at .NET a bit differently and have started being envious of the simplicity of Rails. When it comes to REST the ROR guys have definitely approached it the right way and it is baked right into the Rails framework.
When it comes to .NET there are a few different approaches you can take to create RESTful services.
- ASP.NET MVC
- WCF
- OpenRasta
- Other MVC Frameworks
- Standard HTTP Handlers
In this post I’m going to cover how you can implement such a service using WCF and OpenRasta.
The goal is to expose a Product as JSON and XML using a RESTful uri.
product/1
WCF Approach
First let’s have a look at how to do this in WCF
Project Structure
Just create a standard WCF Service Application Project
DataContract
The first thing we have to do is create a DataContract which is just a Data Transfer Object that you want to be exposed by the web service.
[DataContract] public class Product { [DataMember] public int Id { get; set; } [DataMember] public string Name { get; set; } }
You have to annotate the class with the DataContract attribute and properties with the DataMember attribute.
Service Contract
Now we have to define our Service Contract.
[ServiceContract] public interface ICatalogService { [OperationContract] [WebGet(UriTemplate = "product/{id}", ResponseFormat = WebMessageFormat.Json)] Product GetProductById(string id); }
The important part here is the WebGet attribute. The UriTemplate specifies the route which you can access the Resource on and the ResponseFormat specifies the format or Content-Type to return the resource as, you can choose from Json or XML.
Having to limit to a single Content-Type is already a serious limitation and the UriTemplate being an attribute property makes it hard to change this route easily.
Also notice that the Id argument is a string, this is deliberate as the UriTemplate only supports having path segments of type string. If you try otherwise you will see an exception similar to below.
Operation 'GetProductById' in contract 'ICatalogService' has a path variable named 'id' which does not have type 'string'. Variables for UriTemplate path segments must have type 'string'.
Service Implementation
Next we go to the Service implementation, this is pretty standard.
[AspNetCompatibilityRequirements(RequirementsMode= AspNetCompatibilityRequirementsMode.Allowed)] public class CatalogService : ICatalogService { public Product GetProductById(int id) { var product = new Product { Name = "WCF", Id = 1 }; return product; } }
The only difference here from a standard service implementation is the use of the AspNetCompatibilityRequirements attribute on the class.
Web.Config
The only additional config required is the standardEndpoints configuration. The full config is below.
<system.serviceModel> <standardEndpoints> <webHttpEndpoint> <standardEndpoint defaultOutgoingResponseFormat="Json" name="" helpEnabled="true" automaticFormatSelectionEnabled="true" /> </webHttpEndpoint> </standardEndpoints> <behaviors> <serviceBehaviors> <behavior> <serviceMetadata httpGetEnabled="true"/> <serviceDebug includeExceptionDetailInFaults="false"/> </behavior> </serviceBehaviors> </behaviors> <serviceHostingEnvironment multipleSiteBindingsEnabled="true" /> </system.serviceModel>
Consuming the Service
For the service to return JSON you have to specify in the Content-Type as "application/json” in the HTTP Header, for XML use application/xml.
You can consume using URI http://myrestsite.com/CatalogService.svc/product/1
Observations
Ok well that wasn’t so bad, however I’m sure you’ve already identified a few limitations.
- Inflexible routing options, only one Uri per method
- Only able to return XML or JSON
- Heavy use of Attributes
OpenRasta Approach
OpenRasta deal with Resources, Handlers and Views, but we aren’t concerned with Views in this post.
Handlers
”Handlers are the objects responsible to act upon resources through the HTTP uniform interface and will always get called first. They're the equivalent of Controllers in other MVC frameworks.”
Resources
”Your resources are what you expose on various Uris. In OpenRasta, each thing you want to expose on the web needs to have a Resource class associated with it.”
Project Structure
To host OpenRasta you need to create a standard Web Application project but your Resources and Handlers can be contained in a standard Class Library project.
After installing OpenRasta you’ll need to add references to the following DLLs:
- OpenRasta.dll
- OpenRasta.Codecs.WebForms.dll
- OpenRasta.Hosting.AspNet.dll
The project structure is very simple, you can split it into Handlers and Resources as shown below.
Resource
The Resource or DTO is just a standard POCO.
public class Product { public int Id { get; set; } public string Name { get; set; } }
Notice there’s no base class to inherit or any attributes. This is important as it makes it very easy to utilise Classes that you already have defined.
Handler
Handlers are equivalent to MVC Controllers or compared with WCF it’s the Service implementation.
public class ProductHandler { public object Get(int id) { var product = new Product {Name = "OpenRasta", Id = 1}; return product; } }
Again no base class or attributes required.
OpenRasta Config
One of the great things about OpenRasta is that the Configuration of how the Resources are accessed is completely decoupled from the implementation. The configuration has a nice fluent interface for configuring Handlers and Resources.
You can create a class with any name and implement the OpenRasta.Configuration.IConfigurationSource interface
public class OpenRastaConfig : IConfigurationSource { #region IConfigurationSource Members public void Configure() { using (OpenRastaConfiguration.Manual) { ResourceSpace.Has.ResourcesOfType<Product>() .AtUri("/product/{id}") .HandledBy<ProductHandler>() .AsJsonDataContract() .And .AsXmlDataContract(); } } #endregion }
.AtUri specifies the Uri you can use to access the Resource. It’s also possible to have multiple Uris for the same resource.
.AtUri("/product/{id}") .And .AtUri("/products/{id}")
.HandledBy<ProductHandler> specifies the Handler to use.
.AsJsonDataContract() tells OpenRasta you want to expose the resource as Json
.AsXmlDataContract() tells OpenRasta you want to expose the resource as Xml
Web.Config
There is a little bit of Web.Config configuration to be done as well, this is all you need:
<configuration> <system.web> <compilation debug="true" targetFramework="4.0" /> <httpHandlers> <add verb="*" path="*.rastahook" type="OpenRasta.Hosting.AspNet.OpenRastaHandler, OpenRasta.Hosting.AspNet"/> </httpHandlers> <httpModules> <add name="OpenRastaModule" type="OpenRasta.Hosting.AspNet.OpenRastaModule, OpenRasta.Hosting.AspNet"/> </httpModules> </system.web> <system.webServer> <validation validateIntegratedModeConfiguration="false"/> <modules> <add name="OpenRastaModule" type="OpenRasta.Hosting.AspNet.OpenRastaModule, OpenRasta.Hosting.AspNet"/> </modules> <handlers> <add name="OpenRastaHandler" verb="*" path="*.rastahook" type="OpenRasta.Hosting.AspNet.OpenRastaHandler, OpenRasta.Hosting.AspNet" /> </handlers> </system.webServer> </configuration>
Consuming the Service
As with WCF you need to specify the correct Content-Type to get the format you desire.
You can consume using a URI like http://myrestsite.com/product/1 or http://myrestsite.com/products/1
Observations
All in all OpenRasta is a nicer development experience and is more closely tied to the definition of REST. It is fully extensible and allows to return a Resource in any Format, including an HTML View.
Conclusion
I’ve only touched on a very basic use of OpenRasta, it has tons more functionality which makes developing RESTful services a breeze and also stacks up as a very good MVC framework if you’re having issues with the ASP.NET implementation. It has pretty good documentation and a strong community around it.
Check out the Comparisons table for a round up of features.
IMO doing REST in WCF just doesn’t seem very intuitive to me and is much less flexible especially when you dive deep into using HTTP Status Codes and HTTP Verbs.
If you have a requirement to develop a RESTful service instead of defaulting to WCF I urge you consider OpenRasta instead.
Till next time.