RewriteCond 以任何顺序匹配查询字符串参数
Posted
技术标签:
【中文标题】RewriteCond 以任何顺序匹配查询字符串参数【英文标题】:RewriteCond to match query string parameters in any order 【发布时间】:2012-12-27 18:10:06 【问题描述】:我有一个可能包含三个参数的 URL:
-
?category=计算机
&subcategory=笔记本电脑
&product=dell-inspiron-15
我需要 301 将此 URL 重定向到其友好版本:
http://store.example.com/computers/laptops/dell-inspiron-15/
我有这个,但如果查询字符串参数是任何其他顺序,则无法使其工作:
RewriteCond %QUERY_STRING ^category=(\w+)&subcategory=(\w+)&product=(\w+) [NC]
RewriteRule ^index\.php$ http://store.example.com/%1/%2/%3/? [R,L]
【问题讨论】:
最简单的解决方案是让 PHP 进行重定向。 @Gerben 假设一个人正在使用 PHP ...这个问题是特定于 apache 的,它可以与任意数量的语言/框架/等一起使用。诚然,使用 PHP(或任何其他脚本或编程语言)来操作 URL 参数有一些优势,但这个问题专门针对 apache/mod_rewrite。 在这种特定情况下,OP 似乎正在使用 PHP,因为 RewriteRule 包含 index.php。 ***.com/questions/69290934/… - 任何人都可以帮助解决这个类似的问题 【参考方案1】:1) 如果您只需要检查所有参数是否都在 url 中:
RewriteCond %QUERY_STRING (^|&)category\=computers($|&)
RewriteCond %QUERY_STRING (^|&)subcategory\=laptops($|&)
RewriteCond %QUERY_STRING (^|&)product\=dell\-inspiron\-15($|&)
RewriteRule ^$ http://store.example.com/computers/laptops/dell-inspiron-15/? [R=301,L]
2) 如果您需要精确的参数集:
RewriteCond %QUERY_STRING ^&*(?:category\=computers|subcategory\=laptops|product\=dell\-inspiron\-15)(?!.*&\1(?:&|$))(?:&+(category\=computers|subcategory\=laptops|product\=dell\-inspiron\-15)(?!.*&\1(?:&|$)))2&*$
RewriteRule ^$ http://store.example.com/computers/laptops/dell-inspiron-15/? [R=301,L]
此规则由301 redirect generator生成
【讨论】:
【参考方案2】:我对这类事情采取了稍微不同的方法,利用由 mod_rewrite 设置和读取的 ENV VAR。我发现像这样按名称引用反向引用更具可读性/可维护性,并且这些 ENV VAR 也可以稍后在请求处理中重用。总的来说,我认为这是一种比这里接受的答案更强大、更灵活的方法。无论如何,它对我来说效果很好。我已经完整地复制了我的要点:
来自https://gist.github.com/cweekly/5ee064ddd551e1997d4c
# Mod_rewrite is great at manipulating HTTP requests.
# Using it to set and read temp env vars is a helpful technique.
#
# This example walks through fixing a query string:
# Extract good query params, discard unwanted ones, reorder good ones, append one new one.
#
# Before: /before?badparam=here&baz=w00t&foo=1&bar=good&mood=bad
# After: /after?foo=1&bar=good&baz=w00t&mood=happy
#
# Storing parts of the request (or anything you want to insert into it) in ENV VARs is convenient.
# Note the special RewriteRule target of "-" which means "no redirect; simply apply side effects"
# This lets you manipulate the request at will over multiple steps.
#
# In a RewriteRule, set custom temp ENV VARs via [E=NAME:value]
# Note it's also possible to set multiple env vars
# like [E=VAR_ONE:hi,E=VAR_TWO:bye]
#
# You can read these values using %ENV:VAR_NAMEe <- little "e" is not a typo
#
# Tangent:
# Note you can also read these env vars the same way, if you set them via SetEnvIf[NoCase]
# (It won't work to use SetEnv, which runs too early for mod_rewrite to pair with it.)
#
# Regex details:
# (?:) syntax means "match but don't store group in %1 backreference"
# so (?:^|&) is simply the ^ beginning or an & delimiter
# (the only 2 possibilities for the start of a qs param)
# ([^&]+) means 1 or more chars that are not an & delimiter
RewriteCond %QUERY_STRING (?:^|&)foo=([^&]+)
RewriteRule ^/before - [E=FOO_VAL:%1]
RewriteCond %QUERY_STRING (?:^|&)bar=([^&]+)
RewriteRule ^/before - [E=BAR_VAL:%1]
RewriteCond %QUERY_STRING (?:^|&)baz=([^&]+)
RewriteRule ^/before - [E=BAZ_VAL:%1]
RewriteRule ^/before /after?foo=%FOO_VALe&bar=%BAR_VALe&baz=%BAZ_VALe&mood=happy [R=301,L]
附:这不是针对您的问题的复制/粘贴解决方案,而是准确地显示如何 来处理此类问题。有了这种理解,将其用于您的示例将完全是微不足道的。 :)
【讨论】:
"using %ENV:VAR_NAMEe <- little "e" is not a typo
" - 与 mod_headers(和其他模块)不同,使用 mod_rewrite,您不使用尾随的“e”语法。但是你永远不会同时使用ENV:
前缀和尾随e
。而不是 RewriteRule
substitution 中的 %FOO_VALe
,这应该是 %ENV:FOO_VAL
- 否则你只会在结果中得到一个文字“e”。
还要注意,正如所发布的,这些指令只能在 server 或 virtualhost 上下文中“工作”,而不是.htaccess
,因为RewriteRule
模式上的斜杠前缀。您可能还需要检查查询参数的顺序是否正确,以避免不必要的重定向或潜在的重定向循环。虽然,在上面的代码中,这是通过重定向到不同的 URL 路径来“解决”的——这通常是不可取的。
类似的思路...我写了a more "generic" (drop in) solution 为another question 关于重新排序 URL 参数和丢弃空的或不需要的参数来创建一个规范的 URL。【参考方案3】:
您可以通过多个步骤来实现这一点,方法是检测一个参数,然后转发到下一步,然后重定向到最终目的地
RewriteEngine On
RewriteCond %QUERY_STRING ^category=([^&]+) [NC,OR]
RewriteCond %QUERY_STRING &category=([^&]+) [NC]
RewriteRule ^index\.php$ $0/%1
RewriteCond %QUERY_STRING ^subcategory=([^&]+) [NC,OR]
RewriteCond %QUERY_STRING &subcategory=([^&]+) [NC]
RewriteRule ^index\.php/[^/]+$ $0/%1
RewriteCond %QUERY_STRING ^product=([^&]+) [NC,OR]
RewriteCond %QUERY_STRING &product=([^&]+) [NC]
RewriteRule ^index\.php/([^/]+/[^/]+)$ http://store.example.com/$1/%1/? [R,L]
要避免OR
和双重条件,您可以使用
RewriteCond %QUERY_STRING (?:^|&)category=([^&]+) [NC]
正如@TrueBlue 建议的那样。
另一种方法是在 TestString QUERY_STRING
前加上 & 符号 &
,并始终检查
RewriteCond &%QUERY_STRING &category=([^&]+) [NC]
这种技术(前缀为TestString)也可用于将已经找到的参数传递到下一个RewriteCond
。这让我们可以将三个规则简化为一个
RewriteCond &%QUERY_STRING &category=([^&]+) [NC]
RewriteCond %1!&%QUERY_STRING (.+)!.*&subcategory=([^&]+) [NC]
RewriteCond %1/%2!&%QUERY_STRING (.+)!.*&product=([^&]+) [NC]
RewriteRule ^index\.php$ http://store.example.com/%1/%2/? [R,L]
!
仅用于将已找到并重新排序的参数与QUERY_STRING
分开。
【讨论】:
在第一条规则中,网址更改为例如/computers/
。下一条规则不应该检查 /\w+/
而不是 index.php?
@SalmanA 第一条规则将 URL 更改为 index.php/computers
。所以下一条规则必须检查index.php/...
。我将模式固定为index.php/[^/]+
。
我得到了它的细微调整(例如,使用 (?:^|&)
消除 OR 子句)。
最后一个解决方案似乎只有在所有参数都包含在查询中时才有效。如果缺少一个,它似乎不起作用。有一些简单的解决方法吗?
@CodeX 一个规则的所有RewriteCond
s 必须匹配,因此这在一个规则中是不可能的。如果您只想使用前缀技术,第一个解决方案也可以。这意味着,您可以采用第一个解决方案并将两个 ReplaceCond
s 替换为一个。以上是关于RewriteCond 以任何顺序匹配查询字符串参数的主要内容,如果未能解决你的问题,请参考以下文章
RewriteCond REQUEST_URI - ^ 没有按预期工作
在 Python 中查找输入字符串到元组列表的所有可能匹配项(以任何顺序/顺序)