Siesta: Painless REST via ASP.NET MVC

by Nate on August 10, 2009

Zen uses quite a few open source libraries, and I feel a sense of responsibility to contribute back where I can. To that end, I’m releasing the basic infrastructure that powers the Zen API under the name Siesta.

Siesta is a simple and flexible REST system driven by ASP.NET MVC. While you can take the Siesta bits and use them directly in your app, it’s intended more as an example of how to implement your own system.

The basic idea behind Siesta is to communicate only using models within your application, and allow the infrastructure to handle the serialization and de-serialization, similar to how a view engine manipulates ViewModels or ViewData. Models are just POCOs, and serialization is controlled the [DataContract] and [DataMember] attributes in System.Runtime.Serialization (so yes, this requires .NET 3.5).

Siesta also uses the (sweet-with-extra-awesomesauce) JSON.NET library from James Newton-King. There’s a DataContractJsonSerializer in the BCL now, but as far as I could tell, it doesn’t support indentation. JSON.NET supports both, so it’s a great fit. I’ve also been using it forever, so I’m just used to it – if you deal with JSON formatting, it’s definitely worth a look.

The wire (serialization) format is based on MIME types, and Siesta comes with XML and JSON built-in. However, it’s also extensible by implementing a new IModelSerializer. Speaking of which, if someone wants to implement one for YAML, it would be greatly appreciated. ;)

As described in the Zen API documentation, the wire format is determined by the Content-Type and Accept headers. Content-Type is preferred for incoming data, and Accept is preferred for outgoing data. If a header is missing, the system will fail over to the other one.

Here’s an example of an entity that can be serialized via REST:

[DataContract(Name = "user", Namespace = "")]
public class User : IEntity
{
  [DataMember(Name = "id")]
  public int Id { get; set; }

  [DataMember(Name = "username")]
  public string UserName { get; set; }

  [DataMember(Name = "name")]
  public string Name { get; set; }

  [DataMember(Name = "email")]
  public string Email { get; set; }
}

And its matching controller:

public class UserController : SiestaControllerBase
{
  public IRepository<User> UserRepository { get; private set; }

  public UserController(IRepository<User> userRepository)
  {
    UserRepository = userRepository;
  }

  public ModelResult<User> Get(GetRequest request)
  {
    User user = UserRepository.Get(request.Id);

    if (user == null)
      throw NotFound();

    return Model(user, request.Formatting);
  }

  //...
}

Don’t worry, SiestaControllerBase is just a simple base controller which provides the Model() method shortcut. This (and the IEntity interface you might have noticed on User) are entirely optional, and are actually part of the Siesta.Example assembly rather than the main Siesta assembly. As long as the types you want to communicate with are serializable, they’ll work with Siesta.

In contrast to the Siesta example, in Zen, we actually have separate models (which act as DTOs, and are serialized to/from a wire format) and entities (which are persisted to the database). The example doesn’t show this because it requires a whole separate mapping step, which can get tricky. I would highly recommend that if you use this in a real-world app, you create a separate type hierarchy and map between them.

Anyhow, you can grab the Siesta bits on GitHub, including the full version of this (simple) example. If you have feedback, by all means let me know! There’s actually quite a bit of stuff in Zen that I’d like to release as OSS – it’s just a matter of getting the time to split the code off the product and wrap it up.

Share this post:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • DotNetKicks
  • DZone
  • FriendFeed
  • HackerNews
  • Posterous
  • Reddit
  • Twitter

Related posts:

  1. Functional Magic
  2. Open Source is not a Zero-Sum Game
  3. Frameworks and the Break-Even Point

{ 9 trackbacks }

DotNetKicks.com
August 10, 2009 at 5:29 pm
Twitted by holsee
August 10, 2009 at 5:39 pm
DotNetBurner - ASP.net MVC
August 10, 2009 at 7:24 pm
Dew Drop – August 11, 2009 | Alvin Ashcraft's Morning Dew
August 11, 2009 at 8:06 am
DotNetShoutout
August 11, 2009 at 11:01 am
Reflective Perspective - Chris Alcock » The Morning Brew #410
August 12, 2009 at 3:30 am
Siesta: Painless REST via ASP.NET MVC — Discord&Rhyme
August 16, 2009 at 7:31 pm
Siesta: Painless REST via ASP.NET MVC — Discord&Rhyme | Webmaster Tools
August 16, 2009 at 8:07 pm
RESTify – Extending ASP.NET MVC | I love .NET!
August 24, 2009 at 8:23 pm

{ 9 comments… read them below or add one }

Steven August 10, 2009 at 5:43 pm

Thanks for sharing this with the community, Nate! I have not yet written or worked with REST systems, so please pardon my ignorance. How do you differentiate this UserController with the UserController that your main MVC application uses (if there is one)? Thanks! :)

Nate August 10, 2009 at 5:45 pm

@Steven: Typically you’d have a separate controller, if you already have a UserController in your core application. For example, Zen has a ProjectController and a ProjectApiController.

Steven August 10, 2009 at 6:02 pm

@Nate: That makes sense. Thanks!

Jake Scott August 10, 2009 at 7:20 pm

Hi Nate, apart from unit testing, did you use any firefox plugins or tools to interact/play with the REST interface? Because in the sample I couldn’t think of an easy way to PUT, POST, DELETE etc..

Cheers for releasing this :)
Jake

Nate August 10, 2009 at 7:22 pm

@Jake: Actually, yeah, in the past I’ve used Poster to tinker with REST endpoints. (There’s also a Firefox 3.5-compatible version available here.)

Eddie Cianci August 10, 2009 at 10:40 pm

Hi Nate,

Thanks, this is a great start! (I think the closer we can get to Rails’ notion of RESTful resources, the better.)

As part of .NET 3.5 SP1, can’t we serialize objects without using the Data* / Serializable attributes? http://www.pluralsight.com/community/blogs/aaron/archive/2008/05/13/50934.aspx

Is the case difference (name vs. Name) why you chose to make them explicit?

Nate August 11, 2009 at 12:58 am

@Eddie: Yep, you can safely omit the attributes and the serializers will just use the property names as defined on the type. I like making everything lowercase on the wire, but that’s just personal preference.

Traveller August 11, 2009 at 8:22 am

If you have 2 controllers, one for the views and one for the api, wouldn’t that
force you to repeat the same code just for returning a different ActionResult?

For example, in a get user by id request, wouldn’t you have to repeat the same UserRepository.Get(id)
with all the additional null-checking and what-not in both controllers? (it could be doing a lot more work than that ofc)

On the other hand, I realize the ViewModel maybe somewhat different from the api model. Since I haven’t had any experience building a restful site before, I have no idea how you manage to stay
DRY given the above problem. Thanks for your answer in advance.

Clay Lenhart August 17, 2009 at 3:53 pm

This is great! Thanks for posting it!

Leave a Comment

Previous post: Beginning ASP.NET MVC 1.0

Next post: Open Source is not a Zero-Sum Game