【问题标题】:Reference capture groups of multiple RewriteCond in RewriteRule在 RewriteRule 中引用多个 RewriteCond 的捕获组
【发布时间】:2013-07-21 00:46:52
【问题描述】:

当我有多个 RewriteCond 链接在一起时,只有最后一个 RewriteCond 的捕获组可以用%0-%9 引用。

在下面的问题中,url的查询字符串中的参数可以是任意顺序。要将它们解析为花哨的 url,我需要单独匹配查询字符串中的每个参数:

RewriteCond %{QUERY_STRING} param1=([^&]+)
RewriteCond %{QUERY_STRING} param2=([^&]+)
RewriteCond %{QUERY_STRING} param3=([^&]+)
RewriteRule ^foo$ bar/%1/%2/%3/ [R]

就像我指出的那样……这行不通。为了解决这个问题,我可以在下一个 RewriteCond 中引用前一个 RewriteCond 的捕获组,并将每个参数“传播”到实际的 RewriteRule:

RewriteCond %{QUERY_STRING} param1=([^&]+)
RewriteCond %1&%{QUERY_STRING} ^([^&]*)&.*param2=([^&]+)
RewriteCond %1&%2&%{QUERY_STRING} ^([^&]*)&([^&]*)&.*param3=([^&]+)
RewriteRule ^foo$ bar/%1/%2/%3/ [R]

这应该可以,但是对于每个附加参数,它会变得更加混乱。另一种解决方案可能是解析一个参数并在每个参数之后重定向客户端(导致冗长的重定向链,我想避免这种情况)。

是否有更简洁的方式来访问 RewriteRule 中所有 RewriteCond 的捕获组(例如,是否可以命名它们或将它们分配给一个变量,以便我可以在其他地方引用它们?)

【问题讨论】:

  • +1 提出了一个很好的问题。我也遇到过这个问题,很想知道答案
  • mod_rewrite 文档说 RewriteCond 反向引用仅在当前条件集中保留 last match RewriteCond,所以,是的,您的第二个答案可能是这条路线中的唯一答案.考虑到它的复杂性,您是否考虑过在 foo 上设置一个脚本来解析 GET) 或 POST) 并重定向到适当的 bar

标签: .htaccess mod-rewrite


【解决方案1】:

您可以尝试在重写条件中构造目标 URL:

RewriteCond ##%{QUERY_STRING}      (.*)##(|.*&)param1=([^&]+)
RewriteCond %1/%3##%{QUERY_STRING} (.*)##(|.*&)param2=([^&]+)
RewriteCond %1/%3##%{QUERY_STRING} (.*)##(|.*&)param3=([^&]+)
RewriteCond %1/%3##%{QUERY_STRING} (.*)##(|.*&)param4=([^&]+)
RewriteCond %1/%3##%{QUERY_STRING} (.*)##(|.*&)param5=([^&]+)
RewriteCond %1/%3##%{QUERY_STRING} (.*)##(|.*&)param6=([^&]+)

RewriteRule ^foo$ /bar%1/%3? [L,R]

当我尝试请求时:

/foo?param1=a&param2=b&param6=3&param3=4&param5=5&param4=6

我被重定向到:

/bar/a/b/4/6/5/3

添加任何额外的必需查询字符串参数不会使它看起来比现在更混乱。

【讨论】:

  • ## 的用途是什么?
  • @RahilWazir 它只是用作匹配的分隔符,我们知道我们不能在 uri 中包含 #,所以它可以安全使用
  • 所以这是必要的吗?我的意思是没有分隔符就不行(还是不明白)?
  • @RahilWazir 您需要能够将查询字符串和上一个匹配之间的匹配分开,否则正则表达式只会吞噬所有内容
  • ala 贪婪的正则表达式处理,在 url 中有 ## 并不常见。很有意思!所以 (|.*&) 是可选的捕获开始,以 ([^&]+) 结束,这意味着 ^start of a & 不知道你可以把 ^ 放在中间,或者出于神奇的原因加上 +?跨度>
【解决方案2】:

经过更多的实验,可以将所有参数解析为环境变量并像这样使用它们。我怀疑它是否非常有效,我认为任何需要这种结构的用例都最好使用 php 页面路由器。对于花哨的 url 的 Jon Lin 的解决方案可能会更好。然而,它确实有点模仿我的想法。

不过,我将代码放在这里进行演示:

#Parse all query key-value pairs to an environment variable with the q- prefix
RewriteCond %{QUERY_STRING} ^([^=]*)=([^&]*)(&(.*)|$)$
RewriteRule ^(.*)$ $1?%4 [E=q-%1:%2,N]

#If 'param1' existed in the query string...
RewriteCond %{ENV:q-param1} !^$
RewriteRule ^foo$ bar/%{ENV:q-param1} [END]

甚至……

#Keep the original query string
RewriteCond %{ENV:qstring} ^$
RewriteCond %{QUERY_STRING} ^(.*)$
RewriteRule .* - [E=qstring:#%1]

#parse the query parameters to environment variables
RewriteCond %{ENV:qstring} ^#([^=]*)=([^&]*)(&(.*)|$)$
RewriteRule ^(.*)$ - [E=q-%1:%2,E=qstring:#%4,N]

#See that the original query string is still intact
RewriteCond %{ENV:q-param1} !^$
RewriteRule ^foo$ bar/%{ENV:q-param1} [QSA]

【讨论】:

  • wowsers 是线程安全的吗?!好把戏。我想知道当您在一秒钟内收到 200 个不同的查询时它是否会崩溃?只要不会发生阻塞 io 就可以了。漂亮。
  • 也许 ENV:q-param1 最后应该是 ENV:qstring...
  • 我没有理由相信它不是线程安全的,因为我认为环境变量只针对这个请求持续存在,并且请求由一个线程处理。至于最后一行,那是正确的。它应该表明您可以在不修改查询字符串的情况下执行此操作(例如,/foo?param1=test 将被重写为/bar/test?param1=test。请注意,我不建议在生产环境中使用它,除非您已经测试过它可以执行即使在负载下也很好。它有点滥用 mod_rewrite。
猜你喜欢
  • 1970-01-01
  • 2014-10-20
  • 2017-06-03
  • 1970-01-01
  • 1970-01-01
  • 2010-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多