views:

340

answers:

2

There may be situations where I would need to find an object by parameters other than ID. What is the proper RESTful way to do that?

For example I might want to find a User by username and password, so the strictly RESTful "GET /users/1" wouldn't work.

According to the Rails docs this is the URL pattern for getting all the instances of a resource: "GET /users". I could add parameters to that: "GET /users?username=joe&password=topsecret", but that would be perverting the official purpose of the GET request.

+9  A: 

"GET /users?username=joe&password=topsecret", but that would be perverting the official purpose of the GET request."

No it isn't perverting anything. That's absolutely the correct and RESTful way to do it, and is the reccomended way of retrieving dynamic results in the http spec. REST doesn't care what's in the URL, only that it's unique. The url for that page could be http://3f778a9b8a7c778696e for all REST architecture cares, so long as that's the only way to get there, and it doesn't ever lead anywhere else.

http defines a query string protocol for returning dynamic results. Given the current state of your database, the query string that you give your application ought to always return the same result. Then it will be RESTFUL. URL aesthetics are a different issue from REST altogether.

according to the REST architecture, the rules of the GET request are that it always returns the same results (or maintains the same results for reasonably long periods of time, so that caching works), and that GET Doesn't have side effects. GET needs to be idempotent (always return the same results, regardless of how many times you call it) and not cause the system to change state. That is it.

Of course you don't have to use the query protocol. You can put parameters into forward slashes, inbetween semicolons, or it could be a base64 encoded GUID. It's entirely up to you, as long as it follows those simple rules.

Breton
That's great to know. It sounds as though the Rails docs mislead me. Their example: "GET /photos" means "display a list of all photos". So actually it can mean whatever I want as long as it doesn't change anything about the resource.
Ethan
womble
Assigning meaning to certain url structure is an Aesthetic decision that DHH made. It's "convention over configuration": we guess you're making a crud application, so these are probably the sorts of URLS you'll want. It's completely arbitrary though. They're just identifiers for "static" resources.
Breton
But it's not entirely unreasonable to do such things. Most web servers and ftp servers assign meaning to url structures by mapping the url space to the tree structure of a traditional file system. You can make a scheme that references a 2d or 3d grid space, for example, and still be RESTy.
Breton
Dynamic applications get kind of complicated, and may represent a very interesting and non-traditional topology. Creativity with your urls may be necessary at times.
Breton
disclosure http://bustingseams.blogspot.com/2007/09/how-i-revived-semicolon.html
Breton
whilst this is all perfectly true, and the correct answer given the OP frames the question as a search, it *is* somewhat against convention if this is the standard means of access to the object so I take issue with "so long as that's the only way to get there"
annakata
Your description of a GET always returning the same "results" is misleading. What about the urls GET /CurrentTime GET /CurrentWeather? They are still RESTful even though the results will change.
Darrel Miller
Yes but it's important that the result doesn't depend on the GET request, but instead logically represents the current state of a resource. If the state of the resource hasn't changed, the response shouldn't either. Additionally, it's even more important that the GET doesn't cause a change in state.
Breton
It's easier to understand from the perspective of someone writing a web browser or a proxy. How can I reasonably expect to cache anything from CurrentTime or CurrentWeather if it changes on every request? I should at least have some idea of when to expect it to change. Thus: etag and expires headers
Breton
+2  A: 

Multiple keys is not restful, is it? Perhaps /users/[username]?password=secret. Also, you don't want to use the password there, but some kind of API key, so that an url looks like this:

/users/leethal?secret=da01930adfe82810092

In order to use the username instead of the id, do this:

# controller
@user = User.find_by_username!(params[:id])

# model
def to_param
  username
end
August Lilleaas