mod_rewrite RewriteRule 中的相对替换

kay*_*ahr 8 mod-rewrite .htaccess apache-2.2

我想创建一个独立于网页安装位置的 mod_rewrite RewriteRule。我想在 .htaccess 文件中定义重写规则。让我们以此为例:

RewriteEngine on
RewriteRule ^(.*)\.html html.php
Run Code Online (Sandbox Code Playgroud)

使用此规则,我想将所有 *.html 请求映射到位于 Web 根目录中的 html.php 脚本。问题是,webroot 的公共基本 url 可以更改。因此,Web根目录可以位于http://www.somewhere.tld/或者在一些子目录http://www.somewhere.tld/foo/bar/

但是在重写规则中使用相对路径不起作用。所以我必须写其中之一:

/html.php (When web is located in root directory of the web)
/foo/bar/html.php (When web is located in foo/bar sub directory)
Run Code Online (Sandbox Code Playgroud)

或者我可以设置一个 RewriteBase 但我根本不想配置这个路径。我希望 apache 自动做正确的事情,这样我就可以将 web 复制到某个目录,它就可以工作,而无需告诉 web 所在的重写规则。我该怎么做。?

has*_*nge 12

出于同样的原因,我一直在努力解决同样的问题。我正在尝试使 Web 应用程序独立于它的安装位置,而无需求助于配置脚本或手动用户干预。只需将应用程序放在某个地方,让它做它自己的事。

看来毕竟有一个解决方案,至少对于 Apache 2。需要四行。不过,解释它背后的想法需要超过四行;)

Tl;博士试试这个:

RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond $1#%{REQUEST_URI} ([^#]*)#(.*?)\1$
RewriteRule ^(.*)$ %2index.php [QSA,L]
Run Code Online (Sandbox Code Playgroud)

这是如何以及为什么

  • 我们无法动态设置 RewriteBase,因此我们将其一次性设置为服务器的根 URL:RewriteBase /。这提供了一致性,但这也意味着我们必须自己建立当前目录的 url-path 并将其作为重写的 URL 的前缀。

  • 那么我们在哪个目录?让我们假设一个REQUEST_URI/some/path/app-root/virtual/stuff。我们的 htaccess 在 app-root 中。如果我们获取虚拟部分 - virtual/stuff - 并将其从 中删除REQUEST_URI,我们将留下应用程序目录的 url-path。

  • 捕获虚拟部分很简单,可以在重写规则本身中发生。RewriteRule ^(.*)$ ...使其在$1变量中可用。

  • 现在我们执行我们的小字符串操作并从请求 URI 中删除虚拟部分。我们没有用于此的字符串命令,但 RewriteCond 可以匹配字符串并捕获子字符串。因此,我们将添加一个 RewriteCond,其唯一目的是将 url-path 提取到当前目录。除此之外,“条件”应该远离我们并始终为真。

  • 我们可以$1在 RewriteCond 中使用来自 RewriteRule的变量,因为 mod_rewrite 实际上是向后处理规则集的。它从规则本身的模式开始,如果匹配,则继续检查条件。所以到那时该变量是可用的。

  • 虽然 RewriteCond 中的测试字符串可以使用该变量,但实际条件正则表达式不能。在条件内部,我们只能使用内部反向引用。所以首先,我们组装一个测试字符串“[虚拟部分][某个分隔符][请求 uri]”。'#' 字符是一个很好的分隔符,因为它不会出现在 URL 中。接下来,我们将它与条件匹配

    ([^#]*) - anything up to the separator, captures the virtual part
    #       - the separator
    (.*?)   - anything in the request uri up to what we've captured in group one,
              grabs the current directory url-path
    \1$     - group one again, ie the virtual part of the request uri
    
    Run Code Online (Sandbox Code Playgroud)

    所以这里是完整的条件:RewriteCond $1#%{REQUEST_URI} ([^#]*)#(.*?)\1$.

  • RewriteCond 正则表达式中的第二个捕获组是我们的位置。我们只需要在重写的 URL 前面加上一个%2引用作为前缀。这给我们留下了RewriteRule ^(.*)$ %2index.php [QSA,L]. 瞧。

我还没有进行广泛的测试,但我已经确定它适用于普通虚拟主机以及大量虚拟主机(使用 VirtualDocumentRoot)。这意味着,其他别名位置也应该没问题。

阿帕奇 1.3

不幸的是,Apache 1.3 仍然存在,它将被 RewriteCond 模式扼杀。Apache 1.3 不支持 ungreedy 修饰符('?' in (.*?))。

但是对于 Apache 2,它应该可以解决问题。不过,我绝对会感谢任何反馈,特别是如果它在您的环境中失败。

编辑:我刚刚在我的博客上发布了一篇关于该主题的更全面的文章(“在不知道 RewriteBase 的情况下在 .htaccess 文件中使用 mod_rewrite ”)。请参阅那里了解更多详情。


小智 2

据我所知,你无法实现这一目标。您必须配置 RewriteBase。一种方法是使用 PHP 脚本自动设置 RewriteBase?但这需要(至少)对 .htaccess 的写权限。但您必须在 .htaccess 中配置 RewriteBase。