Changing RESTful URL mappings
Monday, 24 March 2008REST is a style of representing web resources. Rails has popularized the RESTful style, interpreting it into a full set of url patterns and request methods. These patterns have become convention. I’ve got a problem with these conventions.
My issue is when problems arrive when creating or updating a resource. Consider the following scenario for creating a new comment:
- GET: /comment/new is opened, displaying the form for creating a comment
- POST: /comment is called when the empty form is accidentally submitted
- The comment is not saved because of a validation error, but…
- the request is not redirected because the error information would be lost, so…
- the comment/new view is rendered, but the url is /comment
- Should the page be manually refreshed, a different page will display
In this situation, the view corresponds to the action, but the URL doesn’t.
It would be better, instead if:
- The CREATE action mapped to the POST: resource/new method/url pattern.
- The UPDATE action mapped to the PUT: resource/id/edit method/url pattern.
The cool thing is, if you’re using Merb, it’s easy to disagree. But first it’s good to know about Merb’s routing.
Merb’s routing is Rails-inspired, so much of it will appear the same, especially regarding RESTful resources:
- Merb supports namespaces
- Merb supports both #resource and #resources, for specifying either singleton or collection resource
- Merb’s names routes in the same way as Rails (though they’re exposed through the #url method instead of as magic methods)
For specific examples, sometimes it’s quicker to browse the codebase than it is to troll through the docs.
Merb’s routing code is different from Rails, though, in that there is much less magic happening. That makes it easy to change the way it behaves in order to customize the RESTful URL mappings.
The mappings are in merb-core/lib/merb-core/dispatch/router/behavior.rb and are a simple set of regular expressions. Adding the alternate behavior described above is as easy as swapping a few regular expressions:
Behavior.new({ :path => %r[^/?(.:format)?$], :method => :post }, { :action => "create" }, parent),
#...
Behavior.new({ :path => %r[^/:id(.:format)?$], :method => :put }, { :action => "update" }, parent),
... becomes …
Behavior.new({ :path => %r[^/new(.:format)?$], :method => :post }, { :action => "create" }, parent),
# ...
Behavior.new({ :path => %r[^/:id[;/]edit(.:format)?$], :method => :put }, { :action => "update" }, parent),
Extract these changes into a separate file and then include them in init.rb to override Merb’s default behavior. Easy!
Resources:
- Building Web Services the REST Way
- Inspiration from an old discussion with Paul Prescod
Comments
Comments are closed