Resourceful routing in Rails is certainly useful. As is customary in Rails, you get a lot of stuff achieved with a very small amount of declarations. And at first glance it looks like REST! Yay! Except... it will lead one slightly astray. I've come across a few ways in which "resourceful" routing in Rails doesn't really follow REST principles, in ways that aren't merely theoretic but can affect intermediary behavior.
Resourceful routing defines paths or routes for objects that the server developer would like to have manipulated with CRUD operations. The developer can declare an 'invitation' to be a resource, and the routes to index all invitations, create a new invitation, download an invitation, update or delete the invitation, are automatically created. But, problem the first:
- Resourceful routing uses POST to create a new resource. PUT is defined as creating a new resource in HTTP, and intermediaries can use that information-- but only if PUT is used. If POST is used, an intermediary can't tell that a new resources was created.
All routes share common error handling. Route not found? Return 404! But Rails puts the method in as part of the route definition. Thus, problem the second:
- Rails returns 404 Not Found if the client uses an unsupported method on a resource that exists. So for example if I apply resourceful routing such that a resource can be downloaded with GET and updated with PUT, but don't define a POST variant route for that URL, then when the client tries to POST to that URL the server returns 404 because the route (with correct method) was not found. In theory an intermediary could mark the resource as missing and delete its cached representation. Instead, there's a perfectly good error to use in HTTP when a method is not supported on a URL, and that is 405 Method Not Allowed.
- URL parameters are mixed with body parameters. This may not cause problems for a POST, where the response typically isn't cachable anyway. But it's a bad choice in for GET requests, where the URL containing parameters affects caching, while other parameters are unseen by intermediaries.
- Query parameters are treated the same as path elements. Routes are defined as paths that can have parameters in them. So if the routes file defines a route for "/v1/store/buy/:product/with_coins", the :product path element could be any string, and Rails will pass that string into the application just as if it were a URL query parameter. However, caches are supposed to work differently if a URL has query parameters than if it does not, so treating them as the same is misleading the developer.