检查子目录中是否存在文件,否则将所有内容重定向到主控制器

检查子目录中是否存在文件,否则将所有内容重定向到主控制器

我想在 Apache 下的文件mod_rewrite中进行.htaccess如下操作:

  • 如果请求任何收到文件或路径(例如foo.txt或)时,检查子目录中是否存在该文件(例如或 )。如果存在,则显示该文件。foo/barpublicpublic/foo.txtpublic/foo/bar
  • 否则
    • 重定向一切至主控制器index.php

我尝试的解决方案是这样的:

<IfModule mod_rewrite.c>

    RewriteEngine On

    # Don't rewrite requests for files in the 'public' directory
    RewriteRule ^(public)($|/) - [L]

    # For all other files first check if they exist in 'public'
    RewriteCond %{DOCUMENT_ROOT}/public%{REQUEST_URI} -f
    RewriteRule ^ public%{REQUEST_URI} [L]

    # Let 'index.php' handle everything else
    RewriteRule . index.php [L]

</IfModule>

不幸的是,它有一个重大缺陷:它在子目录中不起作用,即当.htaccess和所有其他文件从 移动/到 时/sub/。理想情况下,它可以在随意的当然是子文件夹。

有人能帮忙吗?我该如何修复.htaccess才能消除这个缺陷?

答案1

我遇到过类似的问题,我想分发一个行为如您描述的 Web 应用程序:特定目录中有一堆静态文件,可以像访问 Web 应用程序根目录一样访问它们(即尝试/images/pic.png获取其中的图像public/images/pic.png),其他所有内容都映射到控制器脚本。

我遇到的第一个问题是-fRewriteCond不适用于相对路径 - 即,如果“TestPattern”字符串可以解析为相对于指定条件的目录的文件(使用.htaccess),-f仍然会返回“不匹配” (1)

因此,我们需要尝试“理解”文件所在的每个目录前缀是什么.htaccess,并mod_rewrite实际上为我们解决这个问题,并确保RewriteRule始终相对于每个目录前缀.htaccess文件进行评估 - 但不幸的是,它不会以任何可以访问的方式公开该信息RewriteCond(他们真的应该让我们拥有它)。

话虽这么说,显然你可以做一些奇怪的事情来RewriteCond强制“CondPattern”梳理出%{REQUEST_URI}RewriteRule输入之间的关系——阅读这个问题的答案实现所有奇怪的目标。

但是,将这些技巧简化为手头的问题(匹配-f相对于 .htaccess 的正确路径,无论它在哪里),我们得到这个相当简单的事情:

RewriteEngine On

RewriteCond %{REQUEST_URI}::$1 ^(.*?/)(.*)::\2
RewriteCond %{DOCUMENT_ROOT}%1static/%2 -f
RewriteRule ^(.*)$  static/$1 [END]

RewriteRule . index.php [END,QSA]

解释:

中的正则表达式RewriteCond非常有限,因为它无法解析变量和捕获的文本,但它可以使用语法来反向引用自身\<number>。因此,我们首先创建一个由以下内容组成的字符串:

  • 原始请求 URI
  • 我们不希望在 URL 中出现的硬编码分隔符(该::部分)。
  • RewriteRule将匹配的URI 的本地(每个目录)部分- 这是$1部分:RewriteCond可以查看目标的捕获组RewriteRule(2),因此我们捕获整个输入。

然后,我们应用一个正则表达式来解释该请求 URI 和输入之间的关系RewriteRule- 请求 URI 由两部分组成(第一次和第二次捕获),其中第一部分以 结尾,/第二部分与::分隔符后出现的内容相同 -\2只是在同一个正则表达式中引用第二次捕获,并表示它必须相同。

结果是,我们将请求 URI 分解为两个捕获组 - 第一个是mod_rewrite私下享有的本地每个目录前缀,第二个是我们要检查的该目录下的本地路径。第二个条件用于%<number>引用这些捕获,在文档根目录下构建绝对路径(3)。请注意,%1捕获前缀 URI 的前斜杠和尾斜杠。

最后,请注意,我使用的是标志END而不是L(last),因为它更快并且mod_rewrite立即存在。这样,我就可以删除带有存在的行RewriteRule … - [L],只是为了停止行为,而L这实际上不会停止处理:它只是返回开头或使用新 URL 重写规则。END要简单得多。

脚注:

  1. 实际上,相对路径解析在某些条件下确实有效,如该问题的错误报告。它只是不能按预期工作,并且在大多数常见配置中根本不起作用。
  2. 这种看上去很奇怪的向后行为其实很简单:Apache 总是先解析RewriteRule,然后才检查条件是否允许应用它。
  3. Alias顺便说一句,如果您使用或类似方法将服务器 URI 空间的部分映射到文档根目录下的其他位置,则此方法将不起作用。请参阅文档以CONTEXT_DOCUMENT_ROOT了解如何解决此问题。

答案2

您似乎不想访问 /public 文件夹之外的任何内容?如果是这样,这对我来说似乎很容易,而且不需要任何重写(我希望您实际上没有任何理由使用mod_rewrite

  1. 将index.php放在公共文件夹中
  2. 将 DocumentRoot 设置为公共文件夹
  3. 使用 ErrorDocument 将任何未找到的文件重定向到 index.php

如果您有理由使用公共文件夹,那么您可以使用mod_rewrite将除 index.php 之外的任何内容重定向到 public/anything,并且仍然使用 ErrorDocument 重定向到 index.php。

答案3

这套规则对我有用。我省略了对以 /public 开头的路径的检查。

RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}/public%{REQUEST_URI} -f
RewriteRule ^/(.*) /public/$1 [L]

RewriteRule . /index.html [L]

如果/public不在您的文档根目录中,您可以将 %{DOCUMENT_ROOT} 替换为 /public 目录的磁盘路径。 可能CONTEXT_DOCUMENT_ROOT包含文件内容目录的文件路径,除非 中的文件与 中的.htaccess文件相同,否则不会匹配。我通过使用我的等效项作为我的文档根目录来避免整个问题。如果您无法输入,您可以使用来访问它所在的位置。/public/public/publicRewriteCond/publicindex.php/publicAlias

您的方法涉及重写所有匹配项。用作文档根目录的备用规则集../public将是:

别名 /index.php /var/www/index.php RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L]

您还可以使用自定义 404 错误页面处理丢失的文件。

答案4

RewriteBase将其移动到子文件夹时尝试使用:

RewriteBase /subfolder/
# Rules

相关内容