提问人:Pekka 提问时间:5/24/2017 更新时间:6/9/2017 访问量:719
为什么这个 RewriteRule 会改变QUERY_STRING,而REQUEST_URI保持不变?
Why is this RewriteRule altering QUERY_STRING, but leaving REQUEST_URI untouched?
问:
我有一份 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 运行的吗?
我尝试了无数种变体和书中的所有标志,但收效甚微。
答:
您的设置适用于 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]
另请参阅另一个问题。
评论
PHP 中的 和 mod_rewrite 中的不同,所以你不能这样做。在 PHP 中,它始终包含原始 URL。因此,如果您的 CMS 正在处理它,您就无法像这样更改它。REQUEST_URI
REQUEST_URI
您应该将 CMS 设置为使用所需的 URL,而不是像这样尝试增加 CMS 的 URL 重写。
如果你在 PHP 中检查,你将看到最后重写的 URI。REDIRECT_URL
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];
}
评论
.htaccess
/c5.7
.htaccess
index.php