带有单个插入符 (^) 的 Apache RewriteRule 如何与前端控制器结合使用?

How does Apache RewriteRule with a single caret (^) work in combination with front controllers?

这个问题是 的重新开放,因为实际问题还没有被接受的答案回答。

我对 .htaccess 文件中的这些重写规则感到困惑,该文件应该将对不存在的文件和目录的所有请求重定向到前端控制器

# Send Requests To Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]

原来的问题是:插入符号怎么可能匹配整个URL定位锚?

请注意,突出显示的部分是“整体”。我知道插入符匹配一行的开头,因此规则总是被命中,但插入符不消耗任何字符,根据官方 Apache docs,“Substitution[=42=重写规则的 ] 是用 Pattern."

matched 替换原始 URL-path 的字符串]

此外,如果 .htaccess 文件放在一个目录中,代表目录的 URL 的前导 / 不是匹配的一部分(再次,参见Apache docs,“匹配的内容”下方的第二个要点)。

总而言之,如果 URL 类似于 https://my-domain.tld/api/foo,则重写规则看到的相对 URL 是 api/foo,插入符 ^ 匹配开始和替换后我们以 index.phpapi/foo 结束。本质上就是把index.php放在原来的URL.

前面

这是如何运作的?名为 index.phpapi/foo 的文件不存在。我本以为会出现 404 Not Found 结果代码。

# Send Requests To Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]

如您所述,插入符号 (^) 实际上不匹配任何内容。它只是断言 start-of-string,这对所有事情都是成功的(每个请求的 URL-path)。这就是它在这里需要做的一切......成功。

正则表达式 $(end-of-string 锚点)会产生相同的结果。 .?(可选的单个字符)等

but the caret does not consume any characters and according to the official Apache docs, "the Substitution of a rewrite rule is the string that replaces the original URL-path that was matched by Pattern."

它不只是替换“匹配”的部分。它替换了匹配(或满足)模式的整个 URL-path。模式 ^ 成功匹配(或满足)所有内容。

Moreover, if the .htaccess file is placed in a directory, the leading / of the URL which represents the directory, is not part of the match (again, see Apache docs, 2nd bullet point below "What is matched").

是的。 (尽管严格来说,删除的是 directory-prefix。并且 directory-prefix 始终以斜杠结尾。directory-prefix 是 [=17 所在位置的绝对文件系统路径=] 文件。)

and after substitution we end up with index.phpapi/foo. Essentially, index.php is put in front of the original URL.

不,事实并非如此。

如上所述,成功时 substitution 字符串会替换 entire URL-path。 index.php 完全替换了 api/fooapi/foo 成功匹配(或满足)正则表达式 ^.

如果您真的只想替换 RewriteRule 模式 匹配的 URL-path 部分,那么您需要通过捕获 URL-path 的其他部分来手动重建整个 URL-path。 (当您只想替换所请求的 URL-path 中的单个单词时,这是一项常见任务。)

end up with index.phpapi/foo

要做到这一点,您确实需要 匹配 一切,捕获反向引用并构建 URL-path。例如:

:
RewriteRule (.*) index.php [L]

但是正如您所说,这可能会导致 404。


旁白:

严格来说,^在这里不是最优的。这对目录本身是成功的(空 URL-path)。但是,第一个 conditionRewriteCond 指令)排除了目录,所以无论如何规则都不成功。 模式 不需要对所有内容都成功,只需对目录本身以外的所有内容都成功。例如,以下将是一个改进(即 fail early):

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.php [L]

这只“匹配”了一个字符。它对这场比赛没有任何作用,它只是“成功”。它无法匹配目录本身(但第一个条件也会导致规则失败)。

此规则不需要将目录重写为 index.php,因为 mod_dir 在请求目录本身时发出 index.phpDirectoryIndex)的子请求。