Как кодировать специальные символы с помощью mod_rewrite & Apache?
Я хотел бы иметь красивые URL-адреса для моей системы тегов вместе со всеми специальными символами: +
, &
, #
, %
и =
. Есть ли способ сделать это с помощью mod_rewrite без двойного кодирования ссылок?
Я замечаю, что delicious.com и stackoverflow, похоже, способны обрабатывать отдельные кодированные символы. Какая магическая формула?
Вот пример того, что я хочу:
http://www.foo.com/tag/c%2b%2b
Вызов следующего RewriteRule:
RewriteRule ^tag/(.*) script.php?tag=$1
и значение тега будет "С++"
Нормальная работа apache/mod_rewrite не работает так, как будто она превращает знаки плюса в пробелы. Если я дважды закодирую знак плюса на "% 252B", тогда я получу желаемый результат - однако он вызывает беспорядочные URL-адреса и кажется мне довольно взломанным.
Ответы
Ответ 1
Нормальная работа apache/mod_rewrite не работает так, как будто она превращает знаки плюса в пробелы.
Я не думаю, что это происходит совсем. Apache расшифровывает% 2Bs на + s в части пути, так как + является допустимым символом. Он делает это, прежде чем позволить mod_rewrite посмотреть запрос.
Итак, mod_rewrite изменяет ваш запрос '/tag/С++' на 'script.php? tag = С++'. Но в компоненте строки запроса в формате application/x-www-form-encoded правила экранирования очень немного отличаются от правил, применяемых в частях пути. В частности, "+" является сокращением пространства (которое также может быть закодировано как "%20", но это старое поведение, которое мы никогда не сможем изменить сейчас).
Таким образом, код чтения форм PHP получает "С++" и выгружает его в _GET как C-пространство-пространстве.
Похоже, что вокруг этого стоит использовать rewriteflag 'B'. См. http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html#rewriteflags - любопытно, что он использует более или менее тот же пример!
RewriteRule ^tag/(.*)$ /script.php?tag=$1 [B]
Ответ 2
Я не уверен, что понимаю, что вы просите, но флаг NE
(noescape) для директивы Apache RewriteRule
может вас заинтересовать. В принципе, он предотвращает mod_rewrite
от автоматического экранирования специальных символов в шаблоне замещения, который вы предоставляете. Пример, приведенный в документации Apache 2.2,
RewriteRule /foo/(.*) /bar/arg=P1\%3d$1 [R,NE]
который превратит, например, /foo/zed
в перенаправление на /bar/arg=P1%3dzed
, так что script /bar
будет видеть параметр запроса с именем arg
со значением P1=zed
, если он выглядит в его PATH_INFO
(хорошо, что это не реальный параметр запроса, так что мне нужно: -P).
По крайней мере, я думаю, что это работает., Я никогда не использовал этот особый флаг.
Ответ 3
Я, наконец, сделал это с помощью RewriteMap.
Добавлена карта escape в файле httpd.conf
RewriteMap es int: escape
и использовал его в правиле Rewrite
RewriteRule ([^?.]*) /abc?arg1=${es:$1}&country_sniff=true [L]
Ответ 4
Основная проблема заключается в том, что вы переходите от запроса, который имеет одну кодировку (в частности, знак плюса - знак плюса) в запрос, который имеет различную кодировку (знак плюса обозначает пробел). Решение состоит в том, чтобы обойти декодирование, которое делает mod_rewrite, и преобразовать ваш путь непосредственно из необработанного запроса в строку запроса.
Чтобы обойти нормальный поток правил перезаписи, загрузите необработанную строку запроса непосредственно в переменную среды и измените переменную окружения вместо обычного пути перезаписи. Он уже будет закодирован, поэтому нам вообще не нужно беспокоиться о его кодировании, когда мы переводим его в строку запроса. Однако мы хотим, чтобы проценты кодировали знаки плюса, чтобы они были правильно переданы как знаки плюс, а не пробелы.
Правила невероятно просты:
RewriteEngine On
RewriteRule ^script.php$ - [L]
# Move the path from the raw request into _rq
RewriteCond %{ENV:_rq} =""
RewriteCond %{THE_REQUEST} "^[^ ]+ (/path/[^/]+/[^? ]+)"
RewriteRule .* - [E=_rq:%1]
# encode the plus signs (%2B) (Loop with [N])
RewriteCond %{ENV:_rq} "/path/([^/]+)/(.*)\+(.*)$"
RewriteRule .* - [E=_rq:/path/%1/%2\%2B%3,N]
# finally, move it from the path to the query string
# ([NE] says to not re-code it)
RewriteCond %{ENV:_rq} "/path/([^/]+)/(.*)$"
RewriteRule .* /path/script.php?%1=%2 [NE]
Этот тривиальный script.php подтверждает, что он работает:
<input readonly type="text" value="<?php echo $_GET['tag']; ?>" />
Ответ 5
Я встречаю аналогичную проблему для mod_rewrite с + знаком в url. Сценарий, как показано ниже:
у нас есть url со знаком +, нужно переписать как http://deskdomain/2013/08/09/a+b+c.html
RewriteRule ^/(.*) http://mobiledomain/do/urlRedirect?url=http://%{HTTP_HOST}/$1
Действие struts urlRedirect получает параметр url, делает некоторые изменения и использует URL для другого перенаправления. Но в req.getParameter( "url" ) знак + меняется на пустой, содержимое URL-адреса параметра
http://deskdomain/2013/08/09/a b c.html
, которые вызывают перенаправление 404, не найдены. Для решения проблемы (получить помощь из предыдущего ответа) мы используем флаг перезаписи B (escape-обратные ссылки) и NE (noescape)
RewriteRule ^/(.*) http://mobiledomain/do/urlRedirect?url=http://%{HTTP_HOST}/$1 [B,NE]
В B выйдет + на% 2B, NE предотвратит переход от mod_write% 2B к% 252B (двойной escape + знак), поэтому в req.getParameter("url")=http://deskdomain/2013/08/09/a+b+c.html
Я думаю, причина в том, что req.getParameter( "url" ) сделает unescape для нас, знак + может unescape пустым.
Вы можете попробовать unescape% 2B один раз на +, затем unescape + снова на пустой.
"%2B" unescape-> "+" unescape-> " "