web-dev-qa-db-fra.com

Comment encoder des caractères spéciaux en utilisant mod_rewrite et Apache?

J'aimerais avoir de jolies URL pour mon système de marquage avec tous les caractères spéciaux: +, &, #, %, et =. Existe-t-il un moyen de le faire avec mod_rewrite sans avoir à coder deux fois les liens?

Je remarque que delicious.com et stackoverflow semblent être capables de gérer des caractères spéciaux codés individuellement. Quelle est la formule magique?

Voici un exemple de ce que je veux faire:

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

Déclencherait la RewriteRule suivante:

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

et la valeur de tag serait "c ++"

Le fonctionnement normal d'Apache/mod_rewrite ne fonctionne pas comme ceci, car il semble transformer les signes plus en espaces. Si j'encode deux fois le signe plus en '% 252B', alors j'obtiens le résultat souhaité - cependant cela rend les URLs en désordre et me semble assez hacky.

28
Aldie

Le fonctionnement normal d'Apache/mod_rewrite ne fonctionne pas comme ceci, car il semble transformer les signes plus en espaces.

Je ne pense pas que ce soit exactement ce qui se passe. Apache décode les% 2Bs en + s dans la partie chemin car + est un caractère valide là-bas. Il le fait avant de laisser mod_rewrite regarder la requête.

Ainsi, mod_rewrite change votre requête '/ tag/c ++' en 'script.php? Tag = c ++'. Mais dans un composant de chaîne de requête au format codé application/x-www-form, les règles d'échappement sont très légèrement différentes de celles qui s'appliquent dans les parties de chemin. En particulier, '+' est un raccourci pour l'espace (qui pourrait tout aussi bien être encodé que '% 20', mais c'est un vieux comportement que nous ne pourrons jamais changer maintenant).

Ainsi, le code de lecture de formulaire de PHP reçoit le 'c ++' et le sauvegarde dans votre _GET en tant que C-space-space.

Il semble que la solution consiste à utiliser le drapeau de réécriture "B". Voir http://httpd.Apache.org/docs/2.2/mod/mod_rewrite.html#rewriteflags - curieusement, il utilise plus ou moins le même exemple!

RewriteRule ^tag/(.*)$ /script.php?tag=$1 [B]
26
bobince

Je ne suis pas sûr de comprendre ce que vous demandez, mais l'indicateur NE (noescape) de la directive RewriteRule d'Apache pourrait vous intéresser. Fondamentalement, cela empêche mod_rewrite pour échapper automatiquement les caractères spéciaux dans le modèle de substitution que vous fournissez. L'exemple donné dans la documentation Apache 2.2 est

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

qui tournera, par exemple, /foo/zed dans une redirection vers /bar/arg=P1%3dzed, pour que le script /bar verra alors un paramètre de requête nommé arg avec une valeur P1=zed, s'il regarde dans son PATH_INFO (d'accord, ce n'est pas un paramètre de requête réel, alors poursuivez-moi ;-P).

Du moins, je pense que c'est comme ça que ça fonctionne. . . Je n'ai jamais utilisé ce drapeau en particulier.

5
David Z

Je rencontre le problème similaire pour mod_rewrite avec + signe dans l'url. Le scénario comme ci-dessous:

nous avons une URL avec + signe besoin de réécrire comme http://deskdomain/2013/08/09/a+b+c.html

RewriteRule ^/(.*) http://mobiledomain/do/urlRedirect?url=http://%{HTTP_Host}/$1

L'action struts urlRedirect obtient le paramètre url, modifie et utilise l'url pour une autre redirection. Mais dans req.getParameter ("url"), le signe + change pour être vide, le contenu de l'url du paramètre est http://deskdomain/2013/08/09/a b c.html, Ce qui provoque la redirection 404 introuvable. Pour le résoudre (obtenir de l'aide de la réponse précédente) nous utilisons le drapeau de réécriture B (échapper les références) et NE (noescape)

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

Le B, échappera + à% 2B, NE empêchera l'échappement mod_write% 2B à% 252B (double échappement + signe), donc dans req.getParameter("url")=http://deskdomain/2013/08/09/a+b+c.html

Je pense que la raison en est que req.getParameter ("url") fera un échappement pour nous, le signe + peut s'échapper pour se vider. Vous pouvez essayer unescape% 2B une fois pour +, puis unescape + à nouveau pour vider.

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

1
yren

Le problème sous-jacent est que vous passez d'une demande qui a un codage (en particulier, un signe plus est un signe plus) à une demande qui a un codage différent (un signe plus représente un espace). La solution consiste à contourner le décodage effectué par mod_rewrite et à convertir votre chemin d'accès directement de la requête brute à la chaîne de requête.

Pour contourner le flux normal des règles de réécriture, nous chargeons la chaîne de demande brute directement dans une variable d'environnement et modifions la variable d'environnement au lieu du chemin de réécriture normal. Il sera déjà encodé, donc nous n'avons généralement pas à nous soucier de l'encoder lorsque nous le déplaçons dans la chaîne de requête. Ce que nous voulons, cependant, est de coder en pourcentage les signes plus afin qu'ils soient correctement relayés en tant que signes plus et non en espaces.

Les règles sont incroyablement simples:

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]

Ce script.php trivial confirme qu'il fonctionne:

<input readonly type="text" value="<?php echo $_GET['tag']; ?>" />
1
danorton

Je l'ai finalement fait fonctionner avec l'aide de RewriteMap.

Ajout de la carte d'échappement dans le fichier httpd.conf RewriteMap es int: escape

et l'a utilisé dans la règle de réécriture

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