<D <M <Y
Y> M> D>

: InfoQ, the famous "enterprise software development community", has an interview with Sam and me, as well as an excerpt from the book. Enjoy!

Later today on this weblog: still more REST crap!

[Comments] (6) How's My Driving? #1: Microsoft Astoria: Sumana wants me to post some analyses of web services and talk about how they are or could be made more RESTful. Alex Barnett wants me to look at Microsoft's Astoria project (so does Microsoft, apparently, since they named it after where I live). I love it when a plan comes together. Here is some free consulting for Microsoft, based entirely on my readings of white papers ("overview" and "using").

I wrote this entry by going through the two white papers and trying to discern what resources the system exposed, what parts of the uniform interface they support, what representations are served and accepted, and how resources link to each other. This is the same approach I took when analysing the Atom Publishing Protocol for RWS. I'm pretty sure I got everything right, but I'm not totally sure about the association resources. I've presented my findings in a form much like the one I'd like to see if I was trying to figure out how a RESTful web service worked. I hope this helps.

Introduction

Astoria is a framework for a certain class of web services: those that expose access to tables and rows in a database. In this regard it's similar to ActiveResource. The resources exposed might not correspond exactly to the underlying database objects, but they will look like database tables and there will be some kind of mapping to a real database.

The "overview" whitepaper starts out covering some of the ground covered in RWS chapter 11. It points out that Ajax applications have a different architecture than traditional web applications. They're GUI applications where the GUI events are handled by an invisible web service client. Astoria wants to provide the services those clients will access.

Astoria "uses URIs to point to pieces of data" (ie. resources). Astoria's "data services" are "surfaced to the web as a REST-style resource collection that is addressable with URIs and that agents can interact with using the usual HTTP verbs such as GET, POST or DELETE." So far so good. They talk the talk.

Meet the Resources

An Astoria service defines six basic kinds of resources. A lot of them correspond directly to a resource type in ActiveResource and/or the Atom Publishing Protocol. This makes sense because all three are solving similar problems: exposing collections and the objects in the collections.

  1. The "Entity-set list" resource, located at the service root (/data.svc, for example.) This is not explicitly called out, but it has a URI and responds to GET, so it's a resource. This is a simple list of the available entity-sets (see below). It's analagous to the APP service documents.
  2. "Entity-set" resources. These correspond to a table in a database. ActiveResource has these too; they're analagous to APP's collections. An entity-set is identified like so: /data.svc/Customers. Notably, this is where pagination and "order by" are supported.
  3. There are virtual entity-sets which correspond to a query on a database table: /data.svc/Customers[Valuation gteq 5000000]. This is analagous to the GData extensions to the APP that add query capability to APP collections.
  4. "Entity" resources. These correspond to rows in a database table. ActiveResource has these too, and they're analagous to APP's members.

    A entity is identified with its collection name and then some kind of unique index: maybe /data.svc/Customers[4] or /data.svc/Customers[ALFKI]

  5. There are scoped entity-sets which are associated with some entity. /data.svc/Customers[ALFKI]/Orders shows all the "order" entities associated with a particular "customer" entity. /data.svc/Territories[99999]/Employees shows all the "employee" entities associated with a particular "territory" entity.
  6. Finally, for many-to-many relationships there are separate "association" resources. /data.svc/Territories[99999]/Employees[3] shows the relationship between territory 99999 and employee 3. Presumably it's also available as /data.svc/Employees[3]/Territories[99999].

    (Note: the white papers also talk about a non-resource "association" that's part of the state of an entity. It's a link from one entity to another, as part of a relationship that's not many-to-many. For instance, every order associated with Customers[ALFKI] will contain a link in its representation to Customers[ALFKI]. These associations are not resources, because they don't have their own URIs; they only show up in representations of entities. When I say "association" I'm talking about association resources.)

The uniform interface

What do other frameworks do? The APP and Rails define GET and POST on a "collection" resource; and GET, PUT, and DELETE on a "member" resource. The APP defines GET on a service document. Where Astoria has similar resources, it exposes the same interface. The only part that's difficult to explain is the difference between POST on a regular collection and POST on a scoped collection.

The "overview" white paper has a confusing paragraph about Astoria's use of the uniform interface (the one that begins "For URIs that represent a specific entity...") which is either wrong or very poorly worded: I read it as saying that entities respond to POST, which doesn't make sense. Here's what I got from the "using" white paper, in convenient table form:
Resource GET POST PUT DELETE
Entity-set list X - - -
Entity-set (collection) X X (Create a new entity) - -
Virtual entity-set X - - -
Entity (member) X - X X
Scoped entity-set X X (Create an association between two entities) - -
Association X - X X

I'll just explain "Create an association between two entities," shall I? Here's a scoped entity-set: /data.svc/Territories[99999]/Employees. It's a collection of employees, scoped to a particular territory. There's a many-to-many relationship between territories and employees (at least according to the whitepaper). If I want to associate an existing employee with territory 99999, I POST a representation of the employee to the scoped entity-set. (The representation just contains the employee's database ID.) A new resource is created at a URI like /data.svc/Territories[99999]/Employees[3]: it's the relationship between territory 99999 and employee 3.

Representations

"Currently Astoria can represent data in plain XML, JSON (JavaScript Object Notation) and in a subset of RDF+XML." Representations are selected through content negotiation or through a query string parameter.

Incoming representations have the same format as outgoing representations.

Links

So far, so good. But now Astoria must truly pass through the RESTful crucible. How well does it use hypermedia? Pretty well, actually. The "site map" resource links to all the top-level entity-sets. Entity-sets link to entities and to appropriate entity-sets scoped to each entity. A sample XML representation of http://myserver/data.svc/Customers gives the URI to each customer in the list, and an "orders" link to each customer's orders. This is excellent, much better than ActiveResource.

This lame diagram shows my interpretation of how Astoria resources link to each other. There are two minor missing pieces. First, there's no way to get to a virtual entity-set without constructing the URI manually. This is understandable because those URIs can be about as complicated as SQL expressions. You can't design an HTML 4 form that can generate all of them. You could support a simplified version with a HTML 5 or WADL form. I'm also thinking of a series of resources that work like Bugzilla's advanced query builder to support every kind of query building. It might not be worth it.

Second, I get the impression that a scoped entity-set links to the entities inside the set, but not to the corresponding association resources. That would mean if you want to DELETE an association, you need to construct the URI yourself. This is a guess because I didn't see a representation of a scoped collection.

Miscellaneous

As with Yahoo!'s web services, Astoria's JSON representations can be wrapped in a callback function that calls your code. This lets you use the Javascript on Demand hack described in chapter 11, to run code from a foreign web service in your Ajax application. (The "using" white paper calls this JSONP, for JSON with Padding.)

The "overview" whitepaper says the pagination variables are skip and take, but the "using" whitepaper says they're skip and top. Whitepaper bug!

You can create "service operations", which are arbitrary .NET methods exposed through GET requests. The example given is a custom query, but in practice this feature will encourage a REST-RPC style of programming. However, "[i]n the future attributes will be extended so that the specific HTTP method can be controlled." And the hooks for error checking and triggers are exposed in a way I think will promote RESTful design.

Sam's favorite section: what about caching and ETags? "Astoria services also leverage other aspects such as the well-established HTTP caching infrastructure. Data services can be configured to set various caching-related HTTP headers to cache at the web server, client agent, or intermediate agents such as proxies." It sounds like caching works automatically--with a tie-in to the database engine? No mention of ETags in either white paper.

"Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document." I sure hope not.

Verdict

Astoria services are in fact RESTful and resource-oriented. They're very similar to APP services, though not so much that the APP is obviously a better base. The main thing I'd worry about is the tendency for programmers to use the hooks for writing RPC-style code. I'd head this off at the pass with the promised support for service operations triggered by other HTTP methods, some RESTful examples, and maybe a bit of theory. But I don't know the best way to herd Microsoft programmers.

Mushroom Wellington: When we went to England we had a great lunch in Cambridge at a place called Rainbow Cafe. The highlight of my meal was a "champignon torsade", a puff pastry twist with mushrooms. Today one of Sumana's coworkers came over and I made a similar dish for dinner. It turned out incredibly well so I'll give you a recipe.

First I took the puff pastry out to thaw and made the filling. This starts with crimini mushrooms, chopped up and sauteed. Add some salt, pepper, and thyme. Then you chop up some asparagus stalks and steam them.

To those two add a cheese sauce. I made a roux with about a tablespoon each of butter and flour, then added a quarter-cup of milk and stirred in about three ounces of blue cheese.

By this time the puff pastry was thawed. My pastry was folded into fourths so it was natural to turn each fourth into a Wellington. I cut the pastry into fourths and brushed around the edges of each with egg wash. I stuck filling in the middle and folded the pastry over to make a Wellington/burrito shape. 20 minutes at 400 degrees gives delicious mushroom-asparagus pastries.

We ate these with a salad assembled from the farmer's market, and dessert was strawberries from same.


[Main]

Unless otherwise noted, all content licensed by Leonard Richardson
under a Creative Commons License.