【问题标题】:RewriteCond to match query string parameters in any orderRewriteCond 以任何顺序匹配查询字符串参数
【发布时间】:2012-12-27 18:10:06
【问题描述】:

我有一个可能包含三个参数的 URL:

  1. ?category=计算机
  2. &subcategory=笔记本电脑
  3. &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。
  • stackoverflow.com/questions/69290934/… - 任何人都可以帮助解决这个类似的问题

标签: apache .htaccess mod-rewrite redirect url-rewriting


【解决方案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_NAME}e <- 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_VAL}e&bar=%{BAR_VAL}e&baz=%{BAZ_VAL}e&mood=happy [R=301,L]
    

    附:这不是针对您的问题的复制/粘贴解决方案,而是准确地显示如何 来处理此类问题。有了这种理解,将其用于您的示例将完全是微不足道的。 :)

    【讨论】:

    • "using %{ENV:VAR_NAME}e &lt;- little "e" is not a typo" - 与 mod_headers(和其他模块)不同,使用 mod_rewrite,您不使用尾随的“e”语法。但是你永远不会同时使用ENV: 前缀和尾随e。而不是 RewriteRule substitution 中的 %{FOO_VAL}e,这应该是 %{ENV:FOO_VAL} - 否则你只会在结果中得到一个文字“e”。
    • 还要注意,正如所发布的,这些指令只能在 servervirtualhost 上下文中“工作”,而不是.htaccess,因为RewriteRule 模式上的斜杠前缀。您可能还需要检查查询参数的顺序是否正确,以避免不必要的重定向或潜在的重定向循环。虽然,在上面的代码中,这是通过重定向到不同的 URL 路径来“解决”的——这通常是不可取的。
    • 类似的思路...我写了a more "generic" (drop in) solutionanother 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 前加上 & 符号 &amp;,并始终检查

    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/[^/]+
    • 我得到了它的细微调整(例如,使用 (?:^|&amp;) 消除 OR 子句)。
    • 最后一个解决方案似乎只有在所有参数都包含在查询中时才有效。如果缺少一个,它似乎不起作用。有一些简单的解决方法吗?
    • @CodeX 一个规则的所有RewriteConds 必须匹配,因此这在一个规则中是不可能的。如果您只想使用前缀技术,第一个解决方案也可以。这意味着,您可以采用第一个解决方案并将两个 ReplaceConds 替换为一个。
    猜你喜欢
    • 2020-01-26
    • 1970-01-01
    • 2016-01-23
    • 1970-01-01
    • 1970-01-01
    • 2015-11-21
    • 1970-01-01
    • 1970-01-01
    • 2011-03-11
    相关资源
    最近更新 更多