Как кодировать специальные символы, используя mod_rewrite & Apache? - PullRequest
28 голосов
/ 20 января 2009

Я хотел бы иметь красивые URL для своей системы тегов вместе со всеми специальными символами: +, &, #, % и =. Есть ли способ сделать это с mod_rewrite без необходимости двойного кодирования ссылок?

Я заметил, что Delicious.com и StackOverflow, похоже, способны обрабатывать отдельно закодированные специальные символы. Какая волшебная формула?

Вот пример того, что я хочу, чтобы произошло:

http://www.foo.com/tag/c%2b%2b

Запустит следующий RewriteRule:

RewriteRule ^tag/(.*)   script.php?tag=$1

и значение тега будет "c ++"

Нормальная работа apache / mod_rewrite не работает так, как будто она превращает знаки плюс в пробелы. Если я дважды закодирую знак «плюс» в «% 252B», то получу желаемый результат - однако это приводит к грязным URL-адресам и кажется мне довольно хакерским.

Ответы [ 5 ]

26 голосов
/ 20 января 2009

Нормальная работа apache / mod_rewrite не работает так, как будто она превращает знаки плюс в пробелы.

Я не думаю, что это именно то, что происходит. Apache декодирует% 2Bs в + s в части пути, так как + является допустимым символом там. Это делается перед тем, как позволить mod_rewrite просмотреть запрос.

Итак, mod_rewrite меняет ваш запрос '/ tag / c ++' на 'script.php? Tag = c ++'. Но в компоненте строки запроса в формате application / x-www-form-code правила экранирования очень немного отличаются от тех, которые применяются в частях пути. В частности, «+» является сокращением для пробела (который также может быть закодирован как «% 20», но это старое поведение, которое мы никогда не сможем изменить сейчас).

Таким образом, PHP-код для чтения форм получает 'c ++' и выгружает его в _GET как пространство C-space.

Похоже, что обходным путем является использование флага перезаписи 'B'. Смотрите http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html#rewriteflags - любопытно, что он использует более или менее тот же пример!

RewriteRule ^tag/(.*)$ /script.php?tag=$1 [B]
5 голосов
/ 20 января 2009

Я не уверен, что понимаю, о чем вы спрашиваете, но флаг NE (noescape) для директивы Apache RewriteRule может вас заинтересовать. По сути, он не позволяет mod_rewrite автоматически экранировать специальные символы в указанном вами шаблоне подстановки. Пример, приведенный в документации по Apache 2.2:

RewriteRule /foo/(.*) /bar/arg=P1\%3d$1 [R,NE]

, который, например, превратит /foo/zed в перенаправление на /bar/arg=P1%3dzed, так что скрипт /bar увидит параметр запроса с именем arg со значением P1=zed, если он смотрит PATH_INFO (хорошо, это не настоящий параметр запроса, так что подайте в суд ;-P).

По крайней мере, я так думаю. , , Я никогда не использовал этот конкретный флаг сам.

1 голос
/ 01 марта 2014

Я сталкиваюсь с подобной проблемой для 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 (экранирование обратных ссылок) и NE (noescape)

RewriteRule ^/(.*) http://mobiledomain/do/urlRedirect?url=http://%{HTTP_HOST}/$1 [B,NE]

B, будет экранироваться от + до% 2B, NE предотвратит экранирование от mod_write% 2B до% 252B (двойной экранирование + знак), поэтому в req.getParameter("url")=http://deskdomain/2013/08/09/a+b+c.html

Я думаю, причина в том, что req.getParameter ("url") сделает для нас эскейп, знак + может стереться с нуля. Вы можете попробовать unescape% 2B один раз для +, затем unescape + снова, чтобы очистить.

"%2B" unescape-> "+" unescape-> " "

1 голос
/ 15 сентября 2011

Основная проблема заключается в том, что вы переходите от запроса с одной кодировкой (в частности, знак плюс - это знак плюс) в запрос с другой кодировкой (знак плюс представляет пробел). Решение состоит в том, чтобы обойти декодирование, которое выполняет 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']; ?>" />
1 голос
/ 29 апреля 2011

Я наконец-то заставил его работать с помощью RewriteMap.

Добавлена ​​escape-карта в файле httpd.conf RewriteMap es int: escape

и использовал его в правиле перезаписи

RewriteRule ([^?.]*) /abc?arg1=${es:$1}&country_sniff=true [L]
...