The REST-way to check/uncheck like/unlike favorite

2019-03-14 03:28发布

Currently I am developing an API and within that API I want the signed in users to be able to like/unlike or favorite/unfavorite two resources.

My "Like" model (it's a Ruby on Rails 3 application) is polymorphic and belongs to two different resources:

/api/v1/resource-a/:id/likes

and

/api/v1/resource-a/:resource_a_id/resource-b/:id/likes

The thing is: I am in doubt what way to choose to make my resources as RESTful as possible. I already tried the next two ways to implement like/unlike structure in my URL's:

Case A: (like/unlike being the member of the "resource")

PUT /api/v1/resource/:id/like maps to Api::V1::ResourceController#like
PUT /api/v1/resource/:id/unlike maps to Api::V1::ResourceController#unlike

and case B: ("likes" is a resource on it's own)

POST /api/v1/resource/:id/likes maps to Api::V1::LikesController#create
DELETE /api/v1/resource/:id/likes maps to Api::V1::LikesController#destroy

In both cases I already have a user session, so I don't have to mention the id of the corresponding "like"-record when deleting/"unliking".

I would like to know how you guys have implemented such cases!

Update April 15th, 2011: With "session" I mean HTTP Basic Authentication header being sent with each request and providing encrypted username:password combination.

3条回答
贼婆χ
2楼-- · 2019-03-14 03:49

You are in effect defining a "like" resource, a fact that a user resource likes some other resource in your system. So in REST, you'll need to pick a resource name scheme that uniquely identifies this fact. I'd suggest (using songs as the example):

/like/user/{user-id}/song/{song-id}

Then PUT establishes a liking, and DELETE removes it. GET of course finds out if someone likes a particular song. And you could define GET /like/user/{user-id} to see a list of the songs a particular user likes, and GET /like/song/{song-id} to see a list of the users who like a particular song.

If you assume the user name is established by the existing session, as @joelittlejohn points out, and is not part of the like resource name, then you're violating REST's statelessness constraint and you lose some very important advantages. For instance, a user can only get their own likes, not their friends' likes. Also, it breaks HTTP caching, because one user's likes are indistinguishable from another's.

查看更多
劫难
3楼-- · 2019-03-14 04:02

case B is better, and here have a good sample from GitHub API.

  1. Star a repo

    PUT /user/starred/:owner/:repo

  2. Unstar a repo

    DELETE /user/starred/:owner/:repo

查看更多
姐就是有狂的资本
4楼-- · 2019-03-14 04:07

I think the fact that you're maintaining application state on the server (user session that contains the user id) is one of the problems here. It's making this a lot more difficult than it needs to be and it's breaking a REST's statelessness constraint.

In Case A, you've given URIs to operations, which again is not RESTful. URIs identify resources and state transitions should be performed using a uniform interface that is common to all resources. I think Case B is a lot better in this respect.

So, with these two things in mind, I'd propose something like:

PUT /api/v1/resource/:id/likes/:userid
DELETE /api/v1/resource/:id/likes/:userid

We also have the added benefit that a user can only register one 'Like' (they can repeat that 'Like' as many times as they like, and since the PUT is idempotent it has the same result no matter how many times it's performed). DELETE is also idempotent, so if an 'Unlike' operation is repeated many times for some reason then the system remains in a consistent state. Of course you can implement POST in this way, but if we use PUT and DELETE we can see that the rules associated with these verbs seem to fit our use-case really well.

I can also imagine another useful request:

GET /api/v1/resource/:id/likes/:userid

That would return details of a 'Like', such as the date it was made or the ordinal (i.e. 'This was the 50th like!').

查看更多
登录 后发表回答