Rails url helper не кодирует амперсанды

Итак, я пытаюсь использовать хелпер URL-адресов Rails (page_url) для создания URL-адресов, содержащих специальные символы, включая амперсанды. Большинство случаев работают так, как вы ожидали от них:

(rdb:1) page_url('foo', :host => 'host')
"http://host/pages/foo"
(rdb:1) page_url('foo_%_bar', :host => 'host')
"http://host/pages/foo_%25_bar"

Но по какой-то нечетной причине амперсанды не экранируются:

(rdb:1) page_url('foo_&_bar', :host => 'host')
"http://host/pages/foo_&_bar"

И если я их избегу, они будут повреждены:

(rdb:1) page_url('foo_%26_bar', :host => 'host')
"http://host/pages/foo_%2526_bar"

CGI::escape, с другой стороны, избегает их штраф:

(rdb:1) CGI::escape('foo_&_bar')
"foo_%26_bar"

Что происходит, и как мне обойти это? (С чем-то приятнее, чем gsub('&', '%26'), то есть.)

Ответ 1

Я не могу сказать вам более хороший способ справиться с этим, но я могу объяснить, почему это происходит.

Амперсанды не являются недопустимыми символами для URL. В противном случае у вас возникнут проблемы с: " http://host/pages/foo?bar=baz&style=foo_style" или что-то еще.

Изменить: Копая глубже в исходный код, похоже, что Rails использует CGI.escape только для параметров.

Вспомогательные, url-генераторы используют url_for (под обложками), который в конечном итоге вызывает: http://apidock.com/rails/ActionController/Routing/Route/generate Что вызывает материал в глубине sprivate-методов исходного кода... но в итоге заканчивается вызов CGI.escape(сначала посмотрите в actionpack/lib/action_controller/routing/route.rb, затем в actionpack/lib/action_controller/routing/segment.rb)

Конечный результат заключается в том, что на самом URL-адресе rails использует URI.escape, который вообще не обновляет амперсанды:

>> CGI.escape('/my_foo_&_bar')
=> "%2Fmy_foo_%26_bar"
>> URI.escape('/my_foo_&_bar')
=> "/my_foo_&_bar"

В настоящее время вы ничего не можете сделать об этом, не помещая фактический запрос функции в команду rails.

... если у вас нет возможности выбрать использование амперсандов в ваших URL-адресах Вы всегда можете использовать их для всех URL-адресов:

def my_clean_url(the_url)
   return the_url.gsub(/&/,'_')
end
>> my_clean_url('/my_foo_&_bar')
=> "/my_foo___bar"

page_url(my_clean_url('/my_foo_&_bar'))

Ответ 2

Для всех тех, кто пытается просто закодировать что-либо, кроме a-z, A-Z, 0-9 и подчеркивание:

URI.encode(string, /\W/)

Скажите, что у вас есть контент, который может содержать, например. амперсанды, и вы хотите использовать это содержимое в качестве параметра body для ссылки mailto: Без /\W/ амперсанд (который является безопасным символом URI) не будет закодирован и, следовательно, частично разорвет связь.