为什么这个RewriteRule改变了QUERY_STRING,但保持REQUEST_URI不变?

时间:2017-05-24 13:48:47

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

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

Concrete5附带以下关于漂亮网址的基本说明(将所有网址重定向到中央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>

非常简单。

现在我有一组采用

形式的网址
 /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>

当我选择随机外部网址作为重定向目标时,我可以确认RewriteRule被触发。

但无论何时它是如上所述的内部重定向,会发生什么,我在Concrete5中得到404。当我检查传递给它的内容时,我看到了:

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

所以似乎触发了规则并进行了一些重写,但REQUEST_URI保持不变!

为什么?

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

我在书中尝试了很多变化和所有flags,但收效甚微。

3 个答案:

答案 0 :(得分:3)

PHP中的REQUEST_URI与mod_rewrite中的REQUEST_URI不同,所以你不能这样做。在PHP中,它始终包含原始URL。因此,如果您的CMS正在使用它,则无法像这样更改它。

您应该将CMS设置为使用您想要的网址,而不是像这样尝试扩充CMS的网址重写。

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

答案 1 :(得分:2)

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

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

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

答案 2 :(得分:1)

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

在脚本中,我发现重写前REQUEST_URI值。

所以它的值是正常并且它没有被重写是一个红色的鲱鱼 - 它不是假设被重写。 404错误必须归因于其他原因。

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

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]

另见this other question