What's the best to do when you need a verb using REST api?
I'm exposing a REST API, and It's amazingly easy and smooth to work with as long as you do CRUD (Create, Update, Delete). But I have this Tickets which return a list of tickets (get), a Ticket/{id} which get a particular item (get) and an activate method (put) that change the ticket status from not activated to activated.
Now I'm in need to give the开发者_如何学C REST 'consumer' the ability to do something like (in ws will be called: GetAndActivateRandomTicket() and it keeps me wondering, what that should be described as on REST ? Is it a post? A put? A get? The Goal is to get a random amount of tickets and set their status to active. Something like a get & put at the same time but without knowing before hand the {id} for the put.
Should it be /Tickets?activate=true&amount=5 ? What verb? Should I expose a verb instead of a noun? What is the 'best practices' on this issue?
If repeating the operation does something different (e.g., activates a different ticket) then it is not idempotent. Non-idempotent operations always map to POST (or a custom verb) in a RESTful architecture.
Some resources are easily identifiable and exist in the domain. Some however are a bit tricky as you pointed out. But ROA (resource oriented architecture) takes some getting used to. Anything can be made a resource including transactions, sessions and other such non-domain entities :)
In your case you seem to have an 'algorithmic' resource - selecting a random amount of tickets and activating them. I'm sure this 'randomness' has some way of selecting tickets which is not purely random else there'll be wasted computation with getting already activated set of tickets.
So I'm not sure how your activation is happening - does someone select activate against a bunch of tickets (checkboxes) or just part of 'data packet' without human intervention?
You description seems to hint the latter - so a good practice is to do what you just said: Multiple options on the URL:
- /Tickets?amountToActivate=5;activate (Note the semicolon and just the 'word' activate)
- /Tickets?amountToActivate=5&activate=true (Note: I personally feel the above is better since =true is actually redundant, it's an artifact of non-restful URIs where most folks would explicitly state = true - it's as good as just writing 'activate' in the URL (implies true) absence would imply false :)
Your resource IS algorithmic and the human 'consumer of the URL' when reading it would instantly understand the former URL, =true may not be well understood, but that's just me perhaps. There is a tendency to also use the latter since most frameworks are able to parse query parameters and split by '&' and semicolons may just require some work
In case if there is manual intervention you can break it into two parts:
- GET: /Tickets?fetchRandomAmountOfTickets=100 (since it's algorithmic)
- PUT: /Tickets (the activation 'update' part of PUT for list of tickets that you 'GOT' above)
Hope this helps :)
First of all GET should be idempotent and never make any changes to the resource. Activating a resource should be done using a PUT.
What I would do is create a resource URL like /Tickets/Random that as a result to a GET returns an HTTP 303 to redirect the user to a randomly determined actual resource URL like /Tickets/12345. The user can then activate this ticket using a PUT. All the user app needs to know is the /Tickets/Random URL and he can keep on activating tickets as long as there are any there.
I extracted this :
Anything can be made a resource including transactions, sessions and other such non-domain entities :)
and went with :
TicketActivation resource.
[POST] with a query parameter of amount will return a set of random tickets activated. and return the resource url as such that you can get like /ticket/id=1,2,3,4,5 [GET] will return tickets as normaly with an optional filter of id to return multiple tickets [PUT] will use the filter of id also and set activation true or false depending on parameter.
so I can do :
[post]
/ticket/activation/?amount=5
resource returned will be something like /ticket?id=1,2,3,4,5 so I can issue a get on it.
[get]
/ticket?id=1,2,3,4,5
[put]
/ticket/activation?id=1,2,3,4,5&deActivate [OR]
/ticket/activation?id=1,2,3,4,5&activate
I guess this is the most elegant and also RESTfull and clear solution to this problem, I wanted to share for future reference. Also if you think there is a problem with this approach feel free to comment on it.
精彩评论