背景
我正在用不带框架的PHP编写一个Web项目。我使用了各种软件包来编写MVC模式。对于路由部分(我使用了League/Route),我必须在Apache服务器上设置一些重写规则,路由才能正常工作。
公司服务器的设置方式是,所有应用程序代码都位于文档根目录之外,别名用于内部重定向到应用程序。这会在相对路径,路由和URL重写方面带来很多麻烦。
我将把问题分解为几个步骤,并尝试一次理解一个问题。如果要查看我遇到的总体问题,请参阅this question
PART 1
路由器将$_SERVER["REQUEST_URI"]
值与定义的路由进行比较,以找出其映射到的动作。这是路线:
$router->map("GET","/",Hello1Controller::class);
$router->map("GET","/hello2",Hello2Controller::class);
这是导航栏,只是用来演示路线的:
<li><a href="/">Hello 1</a></li>
<li><a href="/hello2">Hello 2</a></li>
这是httpd.conf中的文档根目录和重写规则:
DocumentRoot "/usr/local/var/www/app/public"
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /index.php$1 [L]
我有一个template.twig位于文档根目录的外部(不在公共目录中),并且在template.twig中,我尝试引用一个css文件:
<link href="css/style.css" type="text/css" rel="stylesheet"/>
此css文件位于public/css/style.css
中,因此它位于文档根目录下。
现在,我尝试访问https://localhost
,并且URL被重写为/index.php,从而触发前端控制器。然后,前端控制器将REQUEST_URI(/)映射到Hello1Controller,后者将呈现template.twig。如果单击导航栏上的“ hello 2”,它将成功导航到该页面。
问题:资源引用中的相对路径不起作用:
将为模板中引用的每个资源发出GET请求。 RewriteCond %{REQUEST_FILENAME} !-f
将测试引用的资源是否是文件。但是,%{REQUEST_FILENAME}
只是前面带有/
的相对路径(我想是相对于根),而不是找出之后的完整路径。而且这显然不是文件,导致重写规则被应用于常规文件引用并发送到前端控制器。您可以从日志中看到以下内容:
[rewrite:trace2] init rewrite engine with requested uri /css/style.css,referer: http://localhost/
[rewrite:trace3] applying pattern '^(.*)$' to uri '/css/style.css',referer: http://localhost/
[rewrite:trace4] RewriteCond: input='/css/style.css' pattern='!-f' => matched,referer: http://localhost/
[rewrite:trace2] rewrite '/css/style.css' -> '/index.php/css/style.css',referer: http://localhost/
[rewrite:trace2] local path result: /index.php/css/style.css,referer: http://localhost/
[rewrite:trace2] prefixed with document_root to /usr/local/var/www/app/public/index.php/css/style.css,referer: http://localhost/
请注意,请求的uri是"/css/style.css"
,但是在代码中我使用了相对路径css/style.css
。这使我认为相对路径相对于url中的当前“位置”。不确定是在重写之前还是之后,但是在这种情况下,结果是否相同-相对于根(因为uri的路径部分中没有内容)或index.php(与该目录位于同一目录中)根)。两者都导致我们看到的"/css/style.css"
,而不是/usr/local/var/www/app/public/css/style.css
。
现在,如果将此重写规则应用到每个目录的上下文中,那么它将起作用:%{REQUEST_FILENAME}
现在是目录所附的相对路径,从而使其成为指向文件的完整路径。
DocumentRoot "/usr/local/var/www/app/public"
<Directory "/usr/local/var/www/app/public">
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ /index.php [L]
</Directory>
(必须对RewriteRule进行一些调整才能使hello2正常工作。这不应影响相对路径)
[rewrite:trace3] [perdir /usr/local/var/www/app/public/] strip per-dir prefix: /usr/local/var/www/app/public/css/style.css -> css/style.css,referer: http://localhost/
[rewrite:trace3] [perdir /usr/local/var/www/app/public/] applying pattern '^(.*)$' to uri 'css/style.css',referer: http://localhost/
[rewrite:trace4] [perdir /usr/local/var/www/app/public/] RewriteCond: input='/usr/local/var/www/app/public/css/style.css' pattern='!-f' => not-matched,referer: http://localhost/
[rewrite:trace1] [perdir /usr/local/var/www/app/public/] pass through /usr/local/var/www/app/public/css/style.css,referer: http://localhost/
问题:这只是重写规则中相对路径的默认行为吗?每个目录或.htaccess总是需要它才能工作?
这也是我对相对路径的观察。如果我错了,请纠正我:
客户端代码中的相对路径(在html,css或twig模板中,其中包含的资源只有在传递给客户端后才会解析):
- 如果引用具有相对路径的资源的文件位于文档根目录之内,则相对路径会直观地起作用:它相对于当前文件。
- 如果引用具有相对路径的资源的文件在文档根目录之外(例如,在正在渲染并发送到客户端的模板文件中),则该路径不是相对于当前文件的。它相对于当前URL。也就是说,“ ..”将返回网址中的上一级。这使我无法在这些文件中使用相对路径。
PART 2
现在,我更改服务器设置,以使文档根目录不再位于应用程序内部,而是将应用程序完全置于文档根目录内部。我将app /软链接到www /
DocumentRoot "/usr/local/var/www"
RewriteEngine On
RewriteRule ^/?$ /app/public/index.php [L]
<Directory "/usr/local/var/www/app/public">
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ /index.php [L]
</Directory>
为了使https://localhost
内部重定向到我的应用程序,我添加了另一个重写规则。
现在资源引用不再起作用:
[rewrite:trace2] init rewrite engine with requested uri /css/style.css,referer: http://localhost/
[rewrite:trace3] applying pattern '^/?$' to uri '/css/style.css',referer: http://localhost/
[rewrite:trace1] pass through /css/style.css,referer: http://localhost/
[core:trace3] request authorized without authentication by access_checker_ex hook: /css/style.css,referer: http://localhost/
[core:info] AH00128: File does not exist: /usr/local/var/www/css/style.css,referer: http://localhost/
甚至没有处理每个目录的定义。我尝试了重写规则的各种变体,包括添加和删除[L],更改两个重写规则的顺序,希望每个目录首先执行,用自己的指令包装第一个重写规则,等等。这些工作。 由于相同的原因,导航栏上的“ hello 2”选项卡也无法正常工作-完全不处理每个目录的重写规则。
PART 3
下一步是将应用程序完全移到文档根目录之外,并使用别名在内部重定向到该应用程序。