views:

250

answers:

4

I've got a rails application with RESTful-ish URLs where I need to pass in a collection of strings (tags), and I don't want to use the query string.

Currently I'm using a route similar to /controller/tagged/:tags/foo/:foo/bar/:bar.:format

This requires the 'tags' to be encoded which is a pain and error prone if you want to manually type the URL into something.

Other approaches like /controller/tagged/tag1/tag2/tag3/foo... are difficult in that they are ambiguous (it's not clear where the tags list ends and other parameters start).

How do other people approach this kind of thing and what is your solution for expressing it as a rails route?

A: 

Well you could always POST them.

Byron Whitlock
REST uses POST for a special purpose (creating a new resource), so if he is trying to GET a resource, he wouldn't use POST...
SingleShot
He actually doesn't say what he is doing though :-)
SingleShot
@SingleShot good point, well made :-) Basically I am trying to retrieve a collection of resources filtered by the parameters. They are actually locations - so filtered by tag, origin point, distance, etc. So yes, it should be a GET. I want to be able to cache it tho.
frankodwyer
+2  A: 

Let's say you are trying to GET a resource, perhaps a "List of Questions" and you want to filter the list based on a set of "Tags" (What a great idea!).

Your URL should be the path to the resource. Perhaps /questions would be a good URL. I know you don't want to use URL parameters for your tags, but on a GET, that's what they are for - filtering or customizing the view of a resource. So to filter for a few subjects of interest rather than getting all questions, your URL might look like:

/questions?tag=ruby-on-rails&tag=restful&tag=url-routing

I believe that is the RESTful way. Alternatives may be palatable, but I think they would be violating the "purist" form of REST.

SingleShot
my problem with the query string here is that rails doesn't seem to take any account of it when caching to the page cache. In other words, /questions?tag=foo and /questions?tag=bar will go to the same location in the page cache: /questions
frankodwyer
I will have to deal with this at some point so went off and dug a little. Searching reveals a common theme: use a rails plug-in that allows you to customize your cache keys (allowing params to be part of the key). The question is, does doing so violate REST or is the caching problem rails-specific? Here's the link to the plugin: http://agilewebdevelopment.com/plugins/action_cache
SingleShot
for action caching, I've been able to get that going using vanilla rails like so: caches_action :controller, :expires_in=>1.hour, :cache_path => Proc.new { |c| "control/#{c.params[:bar]}/#{c.params[:foo]}".gsub(/ /,'') }This fudges the parameters into the cache path and makes sure that distinct requests are cached separately. However I'm not sure what version of rails this requires - I'm using 2.3.2 . Plus, I don't know if similar can be done in relation to the page cache.
frankodwyer
+1  A: 

Use the query string. It's there for exactly this purpose: passing encoded arguments to a resource.

Your version using path elements obscures the fact that /controller/tagged is the actual resource being queried, with the tags as parameters to the request.

To see why the path approach isn't RESTful, consider the following two URLs:

http://example.com/controller/tagged/foo/bar

http://example.com/controller/tagged/bar/foo

By the description you gave, these should refer to the same virtual collection of tagged items. However, in a RESTful system, URLs refer unambiguously to one and only one resource. Your path-based addressing associates many URLs with a single result.

rcoder
my problem with the query string (at least the last time I looked at using it), is that it is ignored for caching. I need the responses to go to the page cache.
frankodwyer
btw +1 for the observation that two urls map to the same resource collection - hadn't thought of that. Not sure it is a problem in practice but it would be nice to avoid it.
frankodwyer
In that case, you may need to manage the cache yourself in your controller, instead of caching at the page level. Try something like `cache(:key => "tagged/#{params[:tag].join(',')}") do ...` in your controller action, capturing the result of the query and template rendering necessary to return the list of tagged resources.
rcoder
+1  A: 

Sam Ruby and Leonard Richardson recommend in their book Restful Web Services to separate non-hierarchical data by commas or semicolons (and query variables for algorithmic resources).

jug
can you elaborate on 'and query variables for algorithmic resources'? Didn't get that - any examples?
frankodwyer
Searches for example should be better addressed as www.google.com/search?q=jellyfish than as www.google.com/search/jellyfish
jug