31

我想为我的标记系统提供漂亮的 URL 以及所有特殊字符:+&#%=。有没有办法用 mod_rewrite 做到这一点而不必对链接进行双重编码?

我注意到delicious.com 和stackoverflow 似乎能够处理单独编码的特殊字符。什么是神奇公式?

这是我想要发生的一个例子:

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

将触发以下 RewriteRule:

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

并且标签的值是“c++”

apache/mod_rewrite 的正常操作不是这样工作的,因为它似乎把加号变成了空格。 如果我将加号双重编码为​​“%252B”,那么我会得到想要的结果——但是它会导致 URLS 混乱,而且对我来说似乎很糟糕。

4

5 回答 5

28

apache/mod_rewrite 的正常操作不是这样工作的,因为它似乎把加号变成了空格。

我不认为这是正在发生的事情。Apache 正在将路径部分中的 %2Bs 解码为 +s,因为 + 在那里是一个有效字符。它在让 mod_rewrite 查看请求之前执行此操作。

那么 mod_rewrite 将您的请求 '/tag/c++' 更改为 'script.php?tag=c++'。但是在 application/x-www-form-encoded 格式的查询字符串组件中,转义规则与应用于路径部分的规则略有不同。特别是,'+' 是空格的简写(它也可以编码为 '%20',但这是我们现在永远无法改变的旧行为)。

因此,PHP 的表单阅读代码接收“c++”并将其作为 C-space-space 转储到您的 _GET 中。

看起来解决这个问题的方法是使用重写标志“B”。请参阅http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html#rewriteflags - 奇怪的是它使用或多或少相同的例子!

RewriteRule ^tag/(.*)$ /script.php?tag=$1 [B]
于 2009-01-20T01:36:37.490 回答
5

我不确定我是否理解您的要求,但是您可能会对NEApache 指令的 (noescape) 标志感兴趣。RewriteRule基本上,它可以防止mod_rewrite在您提供的替换模式中自动转义特殊字符。Apache 2.2 文档中给出的示例是

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

例如,这将变成/foo/zed重定向到/bar/arg=P1%3dzed,这样脚本/bar就会看到一个以argvalue命名的查询参数P1=zed,如果它在其中查找PATH_INFO(好吧,这不是真正的查询参数,所以起诉我 ;-P)。

至少,我认为它是这样工作的。. . 我自己从来没有使用过那个特殊的标志。

于 2009-01-20T00:11:26.617 回答
1

我终于在 RewriteMap 的帮助下让它工作了。

在 httpd.conf 文件中添加了转义映射 RewriteMap es int:escape

并在重写规则中使用它

RewriteRule ([^?.]*) /abc?arg1=${es:$1}&country_sniff=true [L]
于 2011-04-29T09:26:40.380 回答
1

根本问题是您正在从具有一种编码(特别是加号是加号)的请求转移到具有不同编码的请求(加号表示空格)。解决方案是绕过 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']; ?>" />
于 2011-09-15T09:13:00.667 回答
1

我在使用 + 登录 url 时遇到了 mod_rewrite 的类似问题。场景如下:

我们有一个带有 + 号的 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-> " "

于 2014-03-01T13:18:15.130 回答