4 ms·
One thing I've always struggled with with REST, is how you should handle additional verbs. As a contrived example, say we have a booking GET /booking/:id
by glenjamin 15y ago
One thing I've always struggled with with REST, is how you should handle additional verbs.
As a contrived example, say we have a booking
GET /booking/:id
And we then want to cancel this booking, more specifically we want to trigger the "cancel" action (transition), which would change the state of the booking from "active" to "cancelled". There are a few possible ways to do this, and I'm not really sure which is "correct" for REST, or whether it actually matters:
POST /cancel (booking = /booking/:id)
PUT /booking/:id (state = cancelled, note this is dictating the resulting state, not the transition)
POST /booking/:id/cancel
CANCEL /booking/:id (custom verb, good/bad ?)
- hammerdr 15y agoPOST /cancellation { :booking_id => 123 } In general, it is not wrong to noun-ify domain concepts such as a cancellation.
- tantalor 15y agoBut only if its a Java application.
- glenjamin 15y agoI can see why this would be the case given the constrains imposed by the the REST architecture, but I don't really see what benefits this has over having some sort of "cancel" action to be performed on the booking resource. Sidenote: Is it "better" to pass booking_id = number, or booking = booking resource URL. Why?
- icebraining 15y ago>I can see why this would be the case given the constrains imposed by the the REST architecture, but I don't really see what benefits this has over having some sort of "cancel" action to be performed on the booking resource. The problem isn't so much REST, but HTTP: using a custom method could have the potential of creating havoc with proxies and such. > Sidenote: Is it "better" to pass booking_id = number, or booking = booking resource URL. Why? I think the ID should be the URL. It decouples the client from the internal IDs, which is not only DRYer (since otherwise you'd effectively have two IDs) as it can help if you need to change the internal ID format.
- deleted 15y ago[deleted]
- rfrey 15y agoDELETE /booking/:id {reason=cancellation} ?
- bodhi 15y agoIt would really depend on the semantics of the booking and the system. Another alternative would be DELETE /booking/:id Which is basically a more correct HTTP version of your last option. Otherwise, I would lean towards your second option (the PUT), as I would think that what you are doing is telling the server what you want the state of the booking to become. What happens server-side based on the PUT isn't really your concern as a client of the API. > more specifically we want to trigger the "cancel" action I'd posit that this is actually something that the API user shouldn't need to care about. Good question though, I've often pondered it myself.
- artsrc 15y agoI think that a canceled booking is still a booking, just a cancelled one. Here is wording from the HTTP spec "However, the server SHOULD NOT indicate success unless, at the time the response is given, it intends to delete the resource or move it to an inaccessible location. " I don't expect the server to move the booking to an inaccessible location so I see this as a state change not a delete. Hence I am thinking PUT, not DELETE.
- bodhi 15y agoExactly, it depends on the semantics. For example, if I'm exposing a salon's schedule as a resource, perhaps when a booking is cancelled it is thrown away (or otherwise made inaccessible). But in your case, you would still want make the cancelled booking available to REST clients, so PUT would be more suitable for you.
- jtchang 15y agoThe resource you are changing is the booking resource identified by /booking/:id I would go with PUT /booking/:id with a status of canceled. This makes sense to me because of the semantics of traditional FSM. You are going from some state (active) to (cancelled). You are not dictating the resulting state, only that you would like to set the status to cancelled. PUT can return an error in which case the transition failed. It can return success and the transition is good.
- glenjamin 15y agoThis is exactly dictating the resulting state. Consider the FSM: States = (active, cancelled); Transitions = [("cancel", active -> cancelled)] If the API says PUT status = cancelled, then we'd look through the transitions and find the one that matches our current state and the target state ("cancel"), then execute the associated code, the side effects of this transition - probably sending an email. We now want to refactor the cancellation model to this: States = (active, cancellation requested, cancelled) Transitions = [("cancel", active -> cancellation requested), ("confirm", cancellation requested -> cancelled)] You can see where I'm going with this already, the transition we want the user to take from the active state is the same, but the additional state means that we either have to change the API for cancellation, or have the PUT status do something other than specify the state we're moving to. This is the reason I firmly believe changes of state should be requested by naming the transition, and why I'm not entirely sold on the "best" way to do this with a "proper" RESTful architecture.
- deleted 15y ago[deleted]
- pyrhho 15y agoMay as well weigh in. How about: POST /booking/:id/cancellation Because you are creating a 'cancellation object' belonging to that booking. Ninja Edit: Really it is about being consistent, and predictable, so that people using your API can guess how it will act. If you are using an API of yours, and can't guess what the correct url/method is, you should change the API. (I forget who said that)
- nikcub 15y agoYou should fall back on thinking about what you are doing to the record in the database. You are updating the state field (ie UPDATE bookings set status='cancelled' where ID=:id) which maps to PUT