使用echo-nginx-module探寻nginx的rewrite模块的break和last差异

前置条件

安装 echo-nginx-module 模块到 nginx 上,安装方法可见我的这篇文章:为已经安装好的nginx动态添加新模块echo-nginx-module

阅读本文者应当对如何修改 nginx 的配置文件以及如何重启 nginx 使其应用新的配置文件有所了解,并且对 server 和 location 这两个基本的模块有所了解。

相关资料

官方对rewrite相关模块的说明

对break和last的提问

由于官方资料有着绝大多数高手写的文档的毛病——有些对于新手必须阐明的东西在高手脑子里已经不言自明,所以就自然地没写。所以这次探寻主要基于”对break和last的提问“这篇中的第二个回答有所改动。

开始

准备

备份正在被使用的 nginx 的配置文件:cp your-nginx-rootpath/conf/nginx.conf your-nginx-rootpath/conf/nginx.conf.bak

nginx.conf动工:注释掉所有正在工作的server模块,确保我们之后添加的用来测试的server模块是唯一可用的server块。

第一次测试

添加如下代码到配置文件中合适的位置。

server {
    server_name localhost;
    root 'html';
    
    location =/example.txt {
        return 404;
    }

    location / {
        echo 'finally matched location /';
    }

    location /notes {
        echo 'finally matched location /notes';
    }

    location /documents {
        echo 'finally matched location /documents';
    }

    rewrite ^/([^/]+.txt)$ /notes/$1;
    rewrite ^/notes/([^/]+.txt)$ /documents/$1;
}

保存对配置文件的修改后,重启 nginx 以保证 nginx 使用新的配置文件。这一行的操作在每次修改nginx的配置文件后都需要完成,但之后不再复述。

在命令行中输入:curl localhost/example.txt

命令行输出:finally matched location /documents

我认为从中可以得到以下信息:

  1. rewrite模块或者说 ngx_http_rewrite_module 模块的指令会先于 location 块的匹配执行,而不是在server内部按照顺序执行,否则应该遇到location =/example.txt就直接输出finally matched location =/example.txt了(echo-nginx-module在同一个请求的两次echo中,后一次echo并不会覆盖掉前一次echo,而是两次echo均输出)。
  2. 如果没有 flag 参数, 默认的 rewrite 模块的指令会按顺序不断执行到全部执行完(URI不符合rewrite要求的正则表达式则跳过该条本质上也是执行了该条),然后去执行权重较低的指令(比如这里的location模块的匹配)。

第二次测试

rewrite ^/([^/]+.txt)$ /notes/$1;后添加breakbreak作为 flag 参数。
添加break还是last,命令行中输入curl localhost/example.txt,得到的结果都是finally matched location /notes

官方文档对last参数的说法如下:

stops processing the current set of ngx_http_rewrite_module directives and starts a search for a new location matching the changed URI;

翻译过来就是说:停止当前层级的 rewrite 模块的指令,然后开始为修改后的URI寻早合适的 location 块进行匹配。

从结果上来看是很符合要求的。

官方文档对于break参数的说法如下:

stops processing the current set of ngx_http_rewrite_module directives as with the break directive;

说它就和break指令一样,同时在原文中对 break 一次是加了超链接的,跳转过去后说明如下:

Stops processing the current set of ngx_http_rewrite_module directives.

If a directive is specified inside the location, further processing of the request continues in this location.

同样也是停止 rewrite 模块的其它指令,但是并没有像 last 参数说会去搜寻匹配的 location 块。那就只能按照我第一次测试时的理解来说了,因为这样说得通——rewrite模块的指令的权重是高于 location 块(指令)的,所以 rewrite 模块的相关指令都是先于 location 块(指令)执行,即使位置在 location 块(指令)之下。这里 break 参数停止了之后的 rewrite 模块的所有指令,但是 location 块(指令)不属于 rewrite 模块,在 rewrite 模块的指令被结束后就自然而然地执行了。

第三次测试

这次我们把 rewrite 的指令移动到 location =/example.txt 块中,先不加任何 flag 参数,如下:

server {
    listen  80 default_server;
    server_name localhost;

    location =/example.txt {
        rewrite ^/([^/]+.txt)$ /notes/$1;
        rewrite ^/notes/([^/]+.txt)$ /documents/$1;
        return 404;
    }

    location / {
        echo 'finally matched location /';
    }

    location /notes {
        echo 'finally matched location /notes';
    }

    location /documents {
        echo 'finally matched location /documents';
    }
}

仍旧curl localhost/example.txt,返回结果是404页面了

<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.16.1</center>
</body>
</html>

这很好解释,因为没有带 flag 参数,rewrite 模块的指令执行完了就继续执行 retrun 那一行的指令,然后return 霸道无比的直接结束了所有后续,返回了404页面。

第四次测试

然后为rewrite ^/([^/]+.txt)$ /notes/$1;添加 break 或 last 参数后再次测试,看看结果会如何。

添加 break 作为 flag 参数

命令行输入:
curl localhost/example.txt
结果:

<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.16.1</center>
</body>
</html>

添加 last 作为 flag 参数

命令行输入:
curl localhost/example.txt
结果:
finally matched location /notes

结果展现到这里,相信大家也明白了,正如官方文档对break指令描述所说,break指令不会终止” further processing “,而last则会立刻为修改后的URI寻找匹配,相当于从头开始传入一个新的UIR,那么它所在的 location 块中其后面的指令就都无效了。

    原文作者:阳光号
    原文地址: https://segmentfault.com/a/1190000020160878
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞