Rails: Routes, url_for, and slashes
Two surprises with Rails today involving URLs and escape sequences.
1. Regular expressions in routes operate on the raw URL.
Suppose you have these routes:
map.connect 'test/:id', :controller => 'test', :id => /[^\/]*/
map.connect 'test/:hi/:lo', :controller => 'test'
The first route has one parameter, and its regular expression matches all strings without a slash. The second route has two parameters separated by slash. You might wonder what the following routes to (note that %2F is an escaped slash):
"/test/foo%2Fbar"
Answer:
>> ActionController::Routing::Routes.recognize_path("/test/foo%2Fbar")
=> {:action=>"index", :id=>"foo/bar", :controller=>"test"}
It is the first route.
Conclusion:
The regexp operates on the raw URL string, but the param passed to the controller is the unescaped URL string.
2. url_for has inconsistent escaping behavior.
url_for
escapes slash (/), ampersand (&), percent (%), and possibly others, but it does not escape comma (,), semi-colon (;), colon (:), plus (+), among others.
Consider our route above:
map.connect 'test/:id', :controller => 'test', :id => /[^\/]*/
Then following path routes to:
>> ActionController::Routing::Routes.recognize_path("/test/foo%2Fbar%3Abaz%3Bquux%2Bhoge%40dur%25blah")
=> {:action=>"index", :id=>"foo/bar:baz;quux+hoge@dur%blah", :controller=>"test"}
Yet this is not reversible using url_for
:
>> id = "foo/bar:baz;quux+hoge@dur%blah"
=> "foo/bar:baz;quux+hoge@dur%blah"
>> app.url_for({:controller => "test", :id => id}) ActionController::RoutingError: No route matches {:action=>"index", :id=>"foo/bar:baz;quux+hoge@dur%blah", :controller=>"test"}
It seems that the regexp is matched against the raw input. Suppose we remove the regexp:
map.connect 'test/:id', :controller => 'test'
Then the output of url_for
is:
>> app.url_for({:controller => "test", :id => id})
=> "http://www.example.com/test/foo%2Fbar:baz;quux+hoge@dur%25blah"
This is unexpected! Some characters come out escaped and others do not.
I am not sure why this is, or whether it was intended.