使用 NGINX 进行条件重写或 try_files?
conditional rewrite or try_files with NGINX?
我在设置条件重写时遇到问题,我一直在尝试将 if
指令(尽管所有来源都表明它是 "evil")与 -f
一起使用切换以检查文件是否存在,但它不起作用。我相信 issue/case 最好用例子来解释,所以这里是:
目录结构
workspace/
myapp/
webroot/
index.php
assets/
baz.js
hello/
foo.js
modules/
hello/
assets/
foo.js
bar.js
预期结果
/ => /workspace/myapp/webroot/index.php
/assets/hello/foo.js => /workspace/myapp/webroot/assets/hello/foo.js
/assets/hello/bar.js => /workspace/myapp/modules/hello/assets/foo.js
/assets/baz.js => /workspace/myapp/webroot/assets/baz.js
总结:
foo.js
仅存在于 modules/hello/assets
文件夹中,并从那里传送。
bar.js
出现在 webroot/assets/hello
和 modules/hello/assets
中,并从 webroot
传递。
(它 hides/overrides modules
中的文件)
baz.js
仅出现在 webroot/assets
中并从那里传送。
现在不能正常工作的部分是:
location /assets/ {
if (-f $uri) {
break;
}
root /workspace/myapp/modules;
rewrite ^/assets/([^/]+)/(.*)$ //assets/ break;
}
即 if
指令,似乎没有任何影响 - bar.js
文件是从 modules
而不是 webroot
.
我是否应该使用 if
?
有什么办法可以用 try_files
来解决这个问题吗?我似乎无法理解这将如何与我似乎无法绕过的 rewrite
一起工作。
请不要建议使用部署脚本或其他方式重组资产 - 由于各种其他原因,这不是一个选项。
我以前在 Apache 上使用过这种模式,而且 NGINX 在大多数方面似乎都更强大,所以我确定这一定是可能的?
一个不绝对的要求是,我 没有 能够用 webroot/assets/hello/foo.js
覆盖 modules/hello/assets/foo.js
- 从 webroot/assets/*
然而,这是一项要求。
答案分为两部分:第一部分解释了为什么您的配置不起作用,第二部分提供了如何解决您的问题的示例。如果您只对解决方案感兴趣,请直接进入第二部分。
问题
首先,请注意 root
指令在 location
块中的位置并不重要。不管放在location
的最上面还是最下面,反正都会影响到整个location
。另外,请记住 rewrite
行末尾的 break
告诉 Nginx 留在当前位置,即使 URI 已成功重写。
话虽如此,让我们看一下您的配置,看看来自 Expected results
的每个请求是如何处理的,以及为什么没有按预期工作。
我们假设在您的配置中没有其他合适的 location
具有更高的优先级。由于来自 Expected results
的每个请求都以 /assets
开头,所有请求都将根据您的 location
中提供的规则进行处理。所以:
/assets/hello/foo.js
root
设置为 /workspace/myapp/modules
。 if
指令将被评估为 false,因为 /assets/hello/foo.js
不存在,因此 break
将不会被执行。最后,最后的 rewrite
会将请求的 URI 从 /assets/hello/foo.js
更改为 /hello/assets/foo.js
,接下来的 break
将告诉 Nginx 留在当前的 location
内。因此 /workspace/myapp/modules/hello/assets/foo.js
将被送达。
/assets/hello/bar.js
此请求的处理方式与上一个请求的处理方式完全相同,因此 /workspace/myapp/modules/hello/assets/bar.js
将得到处理。
/assets/baz.js
再次将 root
设置为 /workspace/myapp/modules
并且 if
被评估为 false。但是这次最后的rewrite
不会改变URI,因为请求不匹配正则表达式。结果 Nginx 将尝试服务 /workspace/myapp/modules/assets/baz.js
并且由于不存在这样的文件,将 return 404.
如您所见,由于以下几个原因,您的配置可能无法如您所愿地工作:
if
总是评估为 false,因为您尝试检查 URI 而不是文件;
- 请求停留在该位置内,因为您在
rewrite
行中用 break
告诉它停留在那里;
root
在此位置始终设置为 /workspace/myapp/modules
,因此无法从其他任何位置提供文件。
解决办法
最简单的解决方案是使用 try_files
:
root /workspace/myapp/webroot;
location /assets/ {
try_files $uri @modules;
}
location @modules {
root /workspace/myapp/modules;
rewrite ^/assets/([^/]+)/(.*)$ //assets/ break;
}
此配置告诉 Nginx 首先在 webroot
文件夹中查找文件,如果没有找到则转到另一个位置的 modules
文件夹。这种方法被认为是最可取的。
另一方面,使用 if
可以让您在一个位置解决问题:
location /assets/ {
root /workspace/myapp; # The parent folder
if (-f $document_root/webroot/$uri) {
rewrite ^(.*)$ /webroot/ break;
}
rewrite ^/assets/([^/]+)/(.*)$ /modules//assets/ break;
}
但是,这种方法被认为是过时的,不推荐使用。
我在设置条件重写时遇到问题,我一直在尝试将 if
指令(尽管所有来源都表明它是 "evil")与 -f
一起使用切换以检查文件是否存在,但它不起作用。我相信 issue/case 最好用例子来解释,所以这里是:
目录结构
workspace/
myapp/
webroot/
index.php
assets/
baz.js
hello/
foo.js
modules/
hello/
assets/
foo.js
bar.js
预期结果
/ => /workspace/myapp/webroot/index.php
/assets/hello/foo.js => /workspace/myapp/webroot/assets/hello/foo.js
/assets/hello/bar.js => /workspace/myapp/modules/hello/assets/foo.js
/assets/baz.js => /workspace/myapp/webroot/assets/baz.js
总结:
foo.js
仅存在于modules/hello/assets
文件夹中,并从那里传送。bar.js
出现在webroot/assets/hello
和modules/hello/assets
中,并从webroot
传递。 (它 hides/overridesmodules
中的文件)baz.js
仅出现在webroot/assets
中并从那里传送。
现在不能正常工作的部分是:
location /assets/ {
if (-f $uri) {
break;
}
root /workspace/myapp/modules;
rewrite ^/assets/([^/]+)/(.*)$ //assets/ break;
}
即 if
指令,似乎没有任何影响 - bar.js
文件是从 modules
而不是 webroot
.
我是否应该使用 if
?
有什么办法可以用 try_files
来解决这个问题吗?我似乎无法理解这将如何与我似乎无法绕过的 rewrite
一起工作。
请不要建议使用部署脚本或其他方式重组资产 - 由于各种其他原因,这不是一个选项。
我以前在 Apache 上使用过这种模式,而且 NGINX 在大多数方面似乎都更强大,所以我确定这一定是可能的?
一个不绝对的要求是,我 没有 能够用 webroot/assets/hello/foo.js
覆盖 modules/hello/assets/foo.js
- 从 webroot/assets/*
然而,这是一项要求。
答案分为两部分:第一部分解释了为什么您的配置不起作用,第二部分提供了如何解决您的问题的示例。如果您只对解决方案感兴趣,请直接进入第二部分。
问题
首先,请注意 root
指令在 location
块中的位置并不重要。不管放在location
的最上面还是最下面,反正都会影响到整个location
。另外,请记住 rewrite
行末尾的 break
告诉 Nginx 留在当前位置,即使 URI 已成功重写。
话虽如此,让我们看一下您的配置,看看来自 Expected results
的每个请求是如何处理的,以及为什么没有按预期工作。
我们假设在您的配置中没有其他合适的 location
具有更高的优先级。由于来自 Expected results
的每个请求都以 /assets
开头,所有请求都将根据您的 location
中提供的规则进行处理。所以:
/assets/hello/foo.js
root
设置为 /workspace/myapp/modules
。 if
指令将被评估为 false,因为 /assets/hello/foo.js
不存在,因此 break
将不会被执行。最后,最后的 rewrite
会将请求的 URI 从 /assets/hello/foo.js
更改为 /hello/assets/foo.js
,接下来的 break
将告诉 Nginx 留在当前的 location
内。因此 /workspace/myapp/modules/hello/assets/foo.js
将被送达。
/assets/hello/bar.js
此请求的处理方式与上一个请求的处理方式完全相同,因此 /workspace/myapp/modules/hello/assets/bar.js
将得到处理。
/assets/baz.js
再次将 root
设置为 /workspace/myapp/modules
并且 if
被评估为 false。但是这次最后的rewrite
不会改变URI,因为请求不匹配正则表达式。结果 Nginx 将尝试服务 /workspace/myapp/modules/assets/baz.js
并且由于不存在这样的文件,将 return 404.
如您所见,由于以下几个原因,您的配置可能无法如您所愿地工作:
if
总是评估为 false,因为您尝试检查 URI 而不是文件;- 请求停留在该位置内,因为您在
rewrite
行中用break
告诉它停留在那里; root
在此位置始终设置为/workspace/myapp/modules
,因此无法从其他任何位置提供文件。
解决办法
最简单的解决方案是使用
try_files
:root /workspace/myapp/webroot; location /assets/ { try_files $uri @modules; } location @modules { root /workspace/myapp/modules; rewrite ^/assets/([^/]+)/(.*)$ //assets/ break; }
此配置告诉 Nginx 首先在
webroot
文件夹中查找文件,如果没有找到则转到另一个位置的modules
文件夹。这种方法被认为是最可取的。另一方面,使用
if
可以让您在一个位置解决问题:location /assets/ { root /workspace/myapp; # The parent folder if (-f $document_root/webroot/$uri) { rewrite ^(.*)$ /webroot/ break; } rewrite ^/assets/([^/]+)/(.*)$ /modules//assets/ break; }
但是,这种方法被认为是过时的,不推荐使用。