【问题标题】:conditional rewrite or try_files with NGINX?使用 NGINX 有条件地重写或 try_files?
【发布时间】:2015-08-03 18:50:26
【问题描述】:

我在设置条件重写时遇到问题,我一直在尝试使用 if 指令(尽管所有来源都表明它是“邪恶的”)和 -f 开关来检查是否存在文件,但它不起作用。我相信这个问题/案例最好通过例子来解释,所以这里是:

目录结构

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/hellomodules/hello/assets 中,并从webroot 传递。 (它隐藏/覆盖modules 中的文件)
  • baz.js 仅存在于 webroot/assets 中并从那里交付。

现在不起作用的部分是:

location /assets/ {
    if (-f $uri) {
        break;
    }
    root     /workspace/myapp/modules;
    rewrite  ^/assets/([^/]+)/(.*)$ /$1/assets/$2 break;
}

if 指令,似乎没有任何影响-bar.js 文件是从modules 而不是webroot 传递的。

我应该使用if 还是不使用?

有什么办法可以用try_files 来解决这个问题吗?我似乎无法理解这将如何与我似乎无法解决的 rewrite 一起工作。

请不要建议使用部署脚本或其他方式重新组织资产 - 由于各种其他原因,这不是一种选择。

我之前在 Apache 上使用过这种模式,NGINX 似乎在大多数方面都更强大,所以我确定这一定是可能的?

一个不是绝对的要求是我没有能够用webroot/assets/hello/foo.js 覆盖modules/hello/assets/foo.js - 但是从webroot/assets/* 提供脚本是一项要求。 p>

【问题讨论】:

  • 你的重写应该使用标志last而不是break

标签: nginx rewrite


【解决方案1】:

答案分为两部分:第一部分解释了为什么您的配置不起作用,第二部分提供了如何解决问题的示例。如果您只对解决方案感兴趣,请直接进入第二部分。

问题

首先,请注意root 指令在location 块中的位置并不重要。不管你把它放在location 的最顶部还是底部,它都会影响整个location。另外,请记住,rewrite 行末尾的 break 告诉 Nginx 保持在当前位置,即使 URI 已成功重写。

话虽如此,让我们看看您的配置,看看来自Expected results 的每个请求是如何处理的,以及为什么没有按预期工作。

假设您的配置中没有其他合适的location 具有更高的优先级。由于来自Expected results 的每个请求都以/assets 开头,因此所有请求都将根据您的location 中提供的规则进行处理。所以:

  1. /assets/hello/foo.js

root 设置为 /workspace/myapp/modulesif 指令将被评估为 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 将被提供。

  1. /assets/hello/bar.js

此请求的处理方式与前一个请求完全相同,因此将处理 /workspace/myapp/modules/hello/assets/bar.js

  1. /assets/baz.js

再次将root 设置为/workspace/myapp/modules,并将if 评估为假。但是这一次最后的rewrite不会改变URI,因为请求不匹配正则表达式。因此,Nginx 将尝试为/workspace/myapp/modules/assets/baz.js 提供服务,并且由于不存在此类文件,因此将返回 404。

如您所见,您的配置可能无法按您希望的方式工作,原因如下:

  • if 始终被评估为 false,因为您尝试检查 URI 而不是文件;
  • 请求停留在该位置,因为您在rewrite 行中使用break 告诉它停留在那里;
  • root 在此位置始终设置为 /workspace/myapp/modules,因此无法从其他任何位置提供文件。

解决方案

  1. 最简单的解决方案是使用try_files:

    root /workspace/myapp/webroot;
    
    location /assets/ {
        try_files $uri @modules;
    }
    
    location @modules {
        root     /workspace/myapp/modules;
        rewrite  ^/assets/([^/]+)/(.*)$ /$1/assets/$2 break;
    }
    

    此配置告诉 Nginx 先在 webroot 文件夹中查找文件,如果没有找到,则转到另一个位置的 modules 文件夹。这种方法被认为是最可取的。

  2. 另一方面,使用if 可以让您在一个位置解决问题:

    location /assets/ {
        root /workspace/myapp; # The parent folder
    
        if (-f $document_root/webroot/$uri) {
            rewrite ^(.*)$ /webroot/$1 break;
        }
    
        rewrite  ^/assets/([^/]+)/(.*)$ /modules/$1/assets/$2 break;
    }
    

    但是,这种方法被认为已过时,不建议使用。

【讨论】:

  • 非常感谢@Ivan!解决方案 1 非常优雅并且完美运行!使用@ 创建无法通过 URL 直接访问的位置的能力是必不可少的 - 我不知道那个,这将非常有用! :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-27
  • 1970-01-01
  • 1970-01-01
  • 2013-11-29
  • 2015-06-18
相关资源
最近更新 更多