tags:

views:

291

answers:

5

In my continuing quest to try and wrap my mind around RESTful-ness, I've come to another place where I'm not sure how to proceed. I set up a thought expiriment for myself where I'd design a simple voting system for a resource, much like how SO allows voting on questions. So, say my resource is an image, and I can get an image by an ID, like so:

http://www.mysite.com/images/123123

And in this example, that returns say, a JSON representation of an image, like so:

{
"URL":"http://www.mysite.com/images/123123.jpg",
"Rep":"100"
}

How would I design a way to "vote" on that image? I'd like two operations; up-vote and down-vote. The client shouldn't know how much weight each carries, because I'd like to have the award for an up-vote/down-vote be decided at the server level so I can change it anytime I like.

My first idea was to have something like this:

http://www.mysite.com/vote/images?image=123123

To that URL, one could POST something like the following:

{
"Vote":"UpVote"
}

But I'm wary of that - to me that says RPC in disguise. Would that be a poor way to design this? If so, what other designs could I try?

A: 

It would seem silly to me to POST a simple response like that wrapped in JSON

Why not something simple like this, you could do it in an AJAX call to make it uber nice..

A GET request formatted as follows

http://www.mysite.com/vote.php?image=123&vote=up

or POST (this example using jQuery)

$.post("http://www.mysite.com/vote.php", {image:"123", vote:"up"});

(Assuming PHP, but whatever applies)

adam
Very bad. Never ever allow user to perform "destructive" actions using GET requrest. What if Google gets hold of this URL and will start hitting it at 500 Hz?
Anton Gogolev
Though I think security issues aren't part of the question discussed here: If you constrain the GET request to authorized users, that shouldn't be a big problem.
Javier
But it's an update - it's clearly a PUT at best, or POST at worst. There is no way that a GET should ever be used to change data, even if it's authorized. It just doesn't make sense.
unforgiven3
I was only commenting on how to do it in a RESTful manner, not with security in mind.
adam
If you're working on the resource images (and not votes) a GET request to trigger a certain action does make sense to me, because you don't acutally do anything with the image itself.
Javier
I don't see how that makes sense, Javier - GET is supposed to return data, never update it. You are doing something to the image when changing it's vote.
unforgiven3
+1  A: 

If you're working with Rails, I'd go for this GET-URLs:

http://www.mysite.com/images/123123/up_vote

and

http://www.mysite.com/images/123123/down_vote

1. Define the actions "up_vote" and "down_vote" in your Images controller and have it increase or decrease the vote value of your image-model-object.

2. Set the following route in config/routes.rb:

map.resources :images, :member => { :up_vote => :get, :down_vote => :get }

...and you're done (more or less ;-)).

Javier
Is that RESTful, though? My understanding was that you shouldn't expose actions to users in a URI, only resources. Plus, wouldn't that make GET an unsafe operation?
unforgiven3
As Anton Gogolev commented to another answer: Yes, in theoretically it does make GET an unsafe operation. But IMHO the same kind of damage could be done if you use a POST request and for me it feels more like a GET request.
Javier
Well, GET/POST aside, isn't it still un-RESTful to expose an action through a URL?
unforgiven3
If you try to design the votes as a resource it might make sense in a theoretical way, but IMHO you would make everything more complicated without the need to do so. You can't put everything easily into a CRUD operation, that's why I would expose the action on the image resource in this case.
Javier
Using GET to do the upvote/downvote would be a _really_ bad idea and would violate the contract of http methods. e.g. Web crawlers would vote!
Darrel Miller
+2  A: 

REST APIs are supposed to represent nouns so I think you've the first part correct: a single image is represented by a single URL (e.g. http://www.mysite.com/images/123123). I'm not sure tacking on /up_vote and /down_vote is the way to go though.

http://www.mysite.com/images/123123 is the object and you want to modify that, not some other URL (unless you were doing http://www.mysite.com/votes/images/123123). I think you should just POST to http://www.mysite.com/images/123123. This makes GET requests inherently non-destructive since it just retrieves the image, preserves a RESTful design, and keeps your URLs clean.

Stuart Childs
...what happens if you allow users to upload/manage those images? in this case you don't want to constrain yourself by using POST to make votes on an image. bottom line: it all depends on the use cases around the image application.
Javier
If users were going to upload images, I would think you'd want them to POST the data to http://www.mysite.com/images/. In that case, /images is the collection of images so you'd want to be adding to that collection.
Stuart Childs
+3  A: 

To be restful you should return something like this

{
"URL":"http://www.mysite.com/images/123123.jpg",
"Rep":"100"
"UpVoteLink":"http://blah, blah, blah",
"DownVoteLink":"http://blah, blah, something else blah",
}

As far as REST is concerned it doesn't matter what the format of the links are. As long as your client knows that it is supposed to do POST to the "UpVoteLink" or "DownVoteLink" it couldn't care less what the format of the URL is.

Also, if you decide in two weeks that you don't like the URLs you picked, you can change them and no-one will care!

Ok, ok, if you really want a suggestion for an url design, how about

POST http://www.mysite.com/UpVotes?url=http://www.mysite.com/images/1234.jpg

POST http://www.mysite.com/DownVotes?url=http://www.mysite.com/images/1234.jpg

What is cool about this design is that you could vote on images that are not even on your site!

Darrel Miller
Hmm - I think I understand, but isn't UpVoteLink an action, not a resource? Couldn't you just have a "VoteLink", and POST to that, and in that request, specify if it's an upvote or a downvote?
unforgiven3
If you think of a vote as a resource then "UpVotes" could be the collection of votes of type up and the resource "DownVotes" is the collection of votes of type down. Maybe I should have named the links UpVotesLink and DownVotesLink but the name of the link really does not affect much.
Darrel Miller
Ahh - interesting! Very cool.
unforgiven3
+3  A: 

In terms of resources, an image is a thing that has a URI (that is what makes it a resource). Further than that, it has a bunch of properties (size, EXIF data, etc).

When you think of the votes for an image, question if the votes are a resource in themselves.

Chances are, doing a GET on /images/23/votes would return a summary or the list of all votes that the UI would use to display next to the image. Whenever you want to change those votes, the resource you're changing is the votes.

To be restful, the client needs to only understand the media type you've designed, not the URIs or the process to follow to vote.

In your example, you'd define a new format used everywhere on your site. To reformulate yoru example, a GET /images/23/votes would return (in xml but you can reformulate it in json):

<votes>
 <link href="/images/23" rel="subject" />
 <form action="/images/23/votes" mediatype="application/json">
   <submit name="Vote" value="Up">Vote up</submit>
   <submit name="Vote" value="Down">Vote down</submit>
 </form>
</votes>

The idea behind this format is that you have a universal way to define how the client sends the data to the server and build the document. All the previous examles that have been shown correctly make the URI dependent on the server. I propose that what you send to the server should be defined in forms that the server sent.

So spend more time defining how yoru json client is going to understand how to build json objects for submission based on a general form language, and you'll find that once this is done, you have very low coupling between cient and server, and the server has the flexibility to change all the specifics without breaking your clients.

serialseb