In his weblog, Peter Williams points out that Rails' default URIs are procedural, rather than RESTful, because they include an action name. I believe Rails' default URL scheme also puts the developer before the user.
In his weblog, Peter Williams points out that Rails' default URLs are procedural, rather than RESTful, because they include an action name, and suggests some alternative approaches. (REST is an architecture style for distributed systems, and a popular way to think about the web.)
For basic example of a Rails URL, Peter gives:
By default Rails will map this URL to the show method on the product controller, passing in 1 as an ID. Peter suggests this is unRESTful because REST says that the web is a web of information resources on which you can take actions like GET, POST, PUT, and DELETE.
A few months ago Frank Sommers and I spent four fun days playing with Rails, and one of the problems I saw we would have trying to apply it to our requirements was in the area of its URLs. David Heinnemeier Hansson showed me Rails a couple of years ago, and I can still remember him gleefully saying, "I like pretty URLs." He worked hard in Rails to prevent developers from creating apps with URLs like:
To make development faster, he also wanted to make sure that by default, developers wouldn't have to configure a mapping from URL to controller and action, hence pretty URLs. The default Rails URLs are indeed prettier than the URLs that come out of a lot of web frameworks, but I believe they could be prettier, because they favor the developer over the user.
Tim Berners Lee suggests that URIs should be opaque:
The only thing you can use an identifier for is to refer to an object. When you are not dereferencing you should not look at the contents of the URI string to gain other information [or do so] as little as possible.
While I believe in this philosophy of opaque URLs when software deals with URLs, for better or for worse, it is not the case with users. The URL is a part of the user interface, and therefore it is an opportunity to communicate with the user about the information architecture of your site. When designed with the user in mind, URLs can help users figure out where they are in the structure of your site. In addition, I agree with Jakob Nielsen that users should be able to hack off part of the URL to move up a level in your information architecture.
In Rails you can, of course, name your controllers with the user's model of the information architecture in mind. However, after that the name of the action slips into the URL. This happens because in Rails the controllers don't have handler methods for the various HTTP methods (such as processGet for a GET request, etc.). Instead you can define any number of handler methods with verb names indicating what they do, like show or add_to_cart. This allows the developer to put related methods into one controller, which allows them to easily factor out common code into private utility methods. These handler method names then show up by default in the URIs, and therefore, the user interface, so that the developer doesn't need to configure them explicitly (convention over configuration). It's one way Rails helps you create web apps quickly (which, I might add, is something users also like).
Ken Arnold: It takes a lot more work to understand the other person than it takes to understand you. You might say, "I have these two things together so I will let the user—the person using the API—manipulate them." But that user didn't say, "Hey, I want to manipulate these two data structures!" Often the user is saying, "I want to get this result." If users could get a result without manipulating the data structures, they'd be happy as clams. If you can make it more natural for them to get that result, the fact that you have to go through 10 times as much work to access those data structures is good; it means you are providing value. Many people are much more likely to think about what they have in hand and what they can do. They think from the implementation out, instead of thinking from the user in.
What was interesting to me when I read Peter Williams' blog post about RESTfulness in URLs, was that I realized that my notion of user-friendly URLs is very much in sync with REST. URLs that provide users with a mental model of your site as a hierarchy of information resources means that each URL maps to a resource. What can you do with that resource? Retrieve a representation of it with GET, and make updates to it with POST.
It's really a tradeoff. Your quote from Ken Arnold is magnific and can be applied to so many things.
How is it a tradeoff? For example, Rails has support for many backends: FastCGI, SCGI, WEBrick, CGI, Mod_Ruby, Mongrel, and counting. :-) This centralization of matters facilitates the support for these backends. Maybe it's a strong point of Rails that's rarely mentioned.
> It's really a tradeoff. Your quote from Ken Arnold is > magnific and can be applied to so many things. > > How is it a tradeoff? For example, Rails has support for > many backends: FastCGI, SCGI, WEBrick, CGI, Mod_Ruby, > Mongrel, and counting. :-) This centralization of matters > facilitates the support for these backends. Maybe it's a > strong point of Rails that's rarely mentioned.
It's also a tradeoff in the sense that the convention helps you get the web app out the door, to the customer, quickly. And getting something sooner rather than later is very user-friendly to the customer, who probably cares far less about what the URLs look like if it means they can get the app twice as fast.
However, the issue with the convention comes from the structure of placing multiple handler methods in the same controller object, none of which really differentiate between POST and GET (as far as I can see). If you had a separate controller for each "resource," which had separate handler methods for POST and GET, then you could have a convention of placing controllers in package hierarchies that match a mental model you want to give to the users.
Our controllers in our new architecture have GET and POST handler methods, but we do explicitly describe what the URL looks like in the DSL we use to define the controller. I kind of prefer it when things are specified, and in our case it really doesn't cost much development time to do so.
Ted Husted has a good quote: "It's been said that query strings are the universal command line." at http://husted.com/struts/catalog.html. I like that metaphor - being able to navigate a site by changing the URL line.
I would also argue that computer users are trained to think in terms of directory hierarchies. And personally, I find it easier to think about a 1 to 1 mapping between the directories on my server and the URL structure, and I think this transparency helps keep the project straightforward. But then, I've done most of my work with Zope, so it's worldview has affected me.
Upon more reflection, I think it boils down to what are the application's requirements? Are there business requirements that require a public fixed URL format? Is the URL-line part of the service contract? Or is the URL line an internal implementation feature that just happens to be "user-friendly".
Much as I like the concept of a friendly URL-line, (and I do enjoy creating URL-line "languages") if the business does not mandate it, its hard to see the rationale in mandating it. With a more REST-aware client and context, the URL-line format could be elevated to a more coherent and formal concept.
> Think there's a lot more lurking here than just pretty > URLs and user friendliness. > > If you have the URL > http://site.com/products/fruit/delete/2 , how to you cache > it? How do you cache a verb? > > This stuff also runs deep into the API's we use - are we > encouraged to place the importance on verbs or nouns? HTTP > / REST is saying "few verbs, many nouns". > > Longer rant in that direction a while back here: > http://www.sitepoint.com/blogs/2005/12/22/mvc-and-web-apps- > oil-and-water/
That's a great blog post, and it led me to many other bits of information that I hadn't seen before.
It is amazing to me how long it has taken the web development community to kind of figure out how to think about the web. Most popular web frameworks seem to lead us in directions that are not quite in the spirit of HTTP, though HTTP is such a flexible protocol that things have worked very well anyway.
Nevertheless, the way I'm currently thinking of our new web app (a network of sites) is that the websites are information spaces. Each URI maps to a resource, a thing, and therefore they'll have user-friendly noun names. When you do a GET on a URI, you get a representation of that resource. That's what Roy Fielding's REST dissertation reminds us that the HTTP 1.1. spec says. Verbs happen when you POST. (We're not doing PUT or DELETE at this point.) I'd like to design the URIs so users can glean insight into the structure of the information space, as in:
GET /forums - gets a resource that's a list of forums GET /forums/weblogs - gets you a list of threads in the Weblogs Forum GET /forums/weblogs/153170 - gets a list of messages in thread number 153170 (this thread) GET /forums/weblogs/153170/200093 - gets your the message by Harry Fuecks, to which I'm replying right now GET /forums/weblogs/153170/200093.edit - gets (only Harry or admins) a form that will allow Harry or admins to edit his post after submitting it
The way I think of POSTing is that you are updating the resource you are posting to. So if I want to add a new message to a forum topic, I'd post to the URI of that forum topic.
POST /forums/weblogs/153170 - adds a new message to this topic (The "add" verb would be submitted as the value of a hidden form field indicating the action.)
Since I'm not doing DELETE, to delete a forum topic, I'd post to the forum resource, which is a list of topics in that forum.
POST /forums/weblogs - deletes a forum topic in the weblogs forum. The "delete" verb would be submitted as a value of a hidden forum field indicating the action, as would 153170, the thread ID to delete. (If you have a real web services client, you could do a DELETE /forums/weblogs/153170 instead).
POST /forums/weblogs/153170/200093 - when the hidden verb is edit, this is how Harry would submit an edited version of his message after submitting it.
In all three cases I'm sending the POST to the resource that's being updated.
So the way I've come to think of this is that each URI does map to a thing, a resource, a noun. And through POSTS I perform actions on those things. You could think of those actions as methods, and the way you indicate which method you want to all is by including a verb in the POST.
I'm not comfortable enough with REST to know if this is RESTful, but trying to understand REST is one of the main things that brought me to this way of thinking. Any comments?
This makes sense to me -- elements in a path represent things. I had never thought of using the '.' syntax to indicate operations, but that works quite nicely from an OO point of view, and then it's very clear that a path is a thing that you may perform a dot operation on, and the two are clearly separated.
> This makes sense to me -- elements in a path represent > things. I had never thought of using the '.' syntax to > indicate operations, but that works quite nicely from an > OO point of view, and then it's very clear that a path is > a thing that you may perform a dot operation on, and the > two are clearly separated.
Well, every URI is a resource, so:
Is a thing too. It is the URI of a resource which happens to be a web page with a form that allows you edit the resource at:
When you use the form and do a post, it won't post to the .edit URI, but to the URI of the resource being updated:
So really, .edit is short for .edit_form, a noun.
I dropped the .html from my URL design after reading Cool URIs Don't Change:
What to leave out: ... File name extension. This is a very common one. "cgi", even ".html" is something which will change. You may not be using HTML for that page in 20 years time, but you might want today's links to it to still be valid.
So if you're not saying things like .html or .gif anymore, then you can use that . for something else. The reason I didn't just say:
Is that I have other things that would follow the slash (in this case, forum thread names), whose names might colide with the form names. So instead, to get a page with a form that allows you to add a new thread to the Weblogs Forum, I'll use: