为什么这个 RewriteRule 会改变QUERY_STRING,而REQUEST_URI保持不变?

Why is this RewriteRule altering QUERY_STRING, but leaving REQUEST_URI untouched?

提问人:Pekka 提问时间:5/24/2017 更新时间:6/9/2017 访问量:719

问:

我有一份 Concrete5 的副本,这是一个基于 PHP 的 CMS,运行在 .example.com

Concrete5 附带了以下漂亮的 URL 的基本说明(将所有 URL 重定向到中央index.php)

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} !^/c5.7
RewriteRule ^.*$ c5.7/$0 [L]    # Concrete5 is running in the c5.7/ subdirectory
</IfModule>

很简单。

现在我有一组采用以下形式的 URL

 /product/{productname}

我需要转发到 Concrete5(虚拟)URL

/products/details?name={productname}

当我在浏览器中手动输入该 URL 时,该 URL 已设置并按预期工作。

所以我在文件中添加了一行,它现在看起来像这样:htaccess

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /

# New rule for products
RewriteCond %{REQUEST_URI} ^/product/
RewriteRule ^product/(.+)$ /products/details?name=$1 [QSA]

RewriteCond %{REQUEST_URI} !^/c5.7
RewriteRule ^.*$ c5.7/$0 [L]

</IfModule>

我可以确认当我选择随机的外部 URL 作为重定向目标时会触发。RewriteRule

但是每当它是像上面这样的内部重定向时,发生的事情是,我在 Concrete5 中得到一个 404。当我检查传递给它的内容时,我看到:

 REQUEST_URI:  /product/my-random-product
 QUERY_STRING: name=my-random-product

因此,该规则似乎被触发并进行了一些重写,但REQUEST_URI保持不变!

为什么?

是因为 PHP 7.1 是通过 CGI 运行的吗?

我尝试了无数种变体和书中的所有标志,但收效甚微。

apache .htaccess mod-rewrite url-rewriting concrete5

评论

0赞 MrWhite 6/4/2017
“将所有 URL 重定向到中央索引 .php” - 您发布的顶部代码段实际上并没有这样做。这个位所做的只是将所有请求重写到子目录。此子目录中可能有另一个文件,然后将 URL 重写为 ..htaccess/c5.7.htaccessindex.php
0赞 Pekka 6/11/2017
对不起,伙计们,没有及时测试解决方案以奖励赏金。如果其中一个对我有用,我会开始另一个并手动授予它。

答:

1赞 LSerni 6/3/2017 #1

您的设置适用于 PHP 7.1 机器(没有 Concrete5)。它确实调用了我刚刚输入的脚本,该脚本位于 /c5.7/products/details 中。所以Apache部分正在工作。

在脚本中,我看到这是重写之前的旧值。REQUEST_URI

所以它的值是正常的,没有被重写是一条红鲱鱼——它不应该被重写。404 错误一定是由于其他原因造成的。

您的 Concrete5 路由应该支持真实 URL,而不仅仅是虚拟 URL,因为 C5 的路由依赖于 REQUEST_URI。如果是这样,您需要为短 URL 创建路由

Route::register('/product/{productname}' ...)

以及一个适当的控制器来获取参数并调用“旧”控制器。

使用 .htaccess 的一种可能性可能是这样,但我不太确定它是否会起作用,因为REQUEST_URI仍然保持不变:

# New rule for products
RewriteCond %{REQUEST_URI} ^/product/
RewriteRule ^product/(.+)$ c5.7/products/details?name=$1 [L,QSA]

否则,您需要执行外部重定向,这将在浏览器中显示 URL:

RewriteRule product/(.*)$ http://.../products/details?name=$1 [QSA]

另请参阅另一个问题

评论

0赞 Pekka 6/3/2017
谢谢 - 很快就会测试这个。
3赞 user2493235 6/7/2017 #2

PHP 中的 和 mod_rewrite 中的不同,所以你不能这样做。在 PHP 中,它始终包含原始 URL。因此,如果您的 CMS 正在处理它,您就无法像这样更改它。REQUEST_URIREQUEST_URI

您应该将 CMS 设置为使用所需的 URL,而不是像这样尝试增加 CMS 的 URL 重写。

如果你在 PHP 中检查,你将看到最后重写的 URI。REDIRECT_URL

2赞 Rei 6/9/2017 #3

PHP 中的REQUEST_URI将始终是原始请求 URI。 因为 LSerni 和 SuperDuperApps 已经对此进行了解释,所以我不会详细说明。 相反,我提供了一个快速的解决方案:修改REQUEST_URI并在 PHP 中添加参数,而不是在 .name.htaccess

将以下代码添加到 Concrete5 的开头,以确保修改REQUEST_URI 在运行任何 Concrete5 代码之前:index.php

if(preg_match('-^/product/([^?]*)-',$_SERVER['REQUEST_URI'],$matches)){
    $_SERVER['REQUEST_URI'] = '/products/details';
    $_GET['name'] = $matches[1];
}

评论

0赞 Pekka 6/10/2017
聪明的主意 - 但我似乎无法在我的(共享)服务器配置中覆盖REQUEST_URI。他们可能安装了 Suhosin 或其他一些安全补丁。太糟糕了!