【04】nginx:rewrite 和变量

时间:2019-07-24 19:30:49   收藏:0   阅读:93

写在前面的话

 

我们前面已经谈了编译安装,基本语法,日志处理,location 匹配,root / alias 的不同效果。这里我们主要谈谈 rewrite(重写)功能,顺便说说 nginx 中自带的变量。在谈日志格式的时候我们已经聊了一些,这里做个补充。

 

 

指令:rewrite

 

rewrite 的实现依赖于我们编译的时候的 PCRE 库,我们可以通过 rewrite 功能实现将 URL 重写的功能。

总的来说,rewrite 能够实现以下:

1. 用户请求到达某个 server ,如果满足 server 内部的 rewrite 的正则匹配,那么 rewrite 将会对用户请求 URI 重写。

2. 重写完成后直接在该 server 内部去匹配 location。

3. 当匹配到 location 后,如果 location 内部又有 rewrite,那么执行 rewrite 后再次在这个 server 内部去匹配 location,直到请求返回。

4. 当然这个过程不是无限的,nginx 对于这样的跳转就支持 10 次,如果过多甚至死循环,则会报 500 错误。

 

基本语法格式:

rewrite regex replacement [flag];

 

说明:

这里的使用  regex 匹配 URI,并将匹配到的 URI 替换成新的 URI(replacement)。如果有多个 rewrite,执行顺序是从上到下依次执行,匹配到一个后匹配并不会终止,会继续匹配下去,直到返回最后一个匹配为止。如果想中途终止,则需要设置 flag 参数。

当然我们上面说的都是重写 URI,如果 placement 中包含了任何协议相关,如:http:// 和 https://,则请求就直接返回 302 重定向终止了。

当然,浏览器在接收到 30x 的状态码后,会再度根据这个返回去请求 rewrite 之后的地址,最终得到所要想要的结果。如果不是 30x 的状态码,则属于 nginx 内部跳转,浏览器不需要再度发起请求。

 

在 rewrite 中有 4 个 flag 参数:

参数说明
last 停止所有 rewrite 相关指令,然后使用新的 URI 进行 location 匹配。
break 停止所有 rewrite 相关指令, 和 last 不同的是,last 接着继续使用新的 URI 匹配 location,而 break 则是直接使用当前的 URI 进行请求处理,能避免重复 rewrite,所有,last 一般在 server 中,break 一般用于 location 中。
redirect 某些时候,URI 中是不包含协议的,如 https://,但是我们依然希望它像带有一样,给浏览器返回 30x 代码,让浏览器发起第二次请求然后获取到正确的结果,这就需要 redirect。
permanent 和 redirect 类似,但是直接返回 301 永久重定向。

 

在 rewrite 中常用的正则:

表达式作用
. 匹配换行符以外任意字符
? 重复0次或者1次
+ 重复1次或多次
* 重复0次或者多次
\d 匹配数字
^ 匹配开始
$ 匹配结束
{n} 重复 n 次
{n,} 重复 n 次或更多次
[c] 匹配单个字符c
[a-z] 匹配 a-z 任意一个小写字母

使用 () 可以将匹配内容括起来,后面使用 $1 来引用,当然,第二个 () 就是 $2。

 

rewrite 示例:

示例1:直接跳转到其它 URL,但是将参数带过去

在 vhosts 下面新建:rewrite-demo.conf 

server {
    listen      8083;
    server_name localhost;
    rewrite_log  on;
    rewrite  ^/(.*) https://www.ezops.com/$1 permanent;
    error_log    /data/logs/nginx/rewrite-error.log;
    access_log   /data/logs/nginx/rewrite-access.log mylog;
}

我们这里开启 rewrite log,这样定向错误会记录到 error_log 中。配置完成后重载 nginx,访问:

http://192.168.100.111:8083/aaa/bbb

结果如下:

技术分享图片

 

示例2:测试 last 和 break,修改刚刚的配置,这里我们用到 nginx 自带变量 uri

server {
    listen      8083;
    server_name localhost;
    rewrite_log  on;
    rewrite  ^/(.*) /hello/$1 last;
    error_log    /data/logs/nginx/rewrite-error.log;
    access_log   /data/logs/nginx/rewrite-access.log mylog;

    location ^~ /hello {
        echo "URI 1: $uri";
        rewrite ^/hello/(.*)  /world/$1 last;
        echo "URI 2: $uri";
    }

    location ^~ /world {
        echo "URI 3: $uri";
    }
}

重载访问:

技术分享图片

此时把 location 中 last 改为 break 测试:

技术分享图片

可以发现:

如果 rewrite 是 last 作为 flag 并不会影响接下来继续去匹配相关的 location,且该 location 下面也就执行了 rewrite 操作,其它的都没有执行到。

但是当 break 作为 flag 的时候,rewrite 就终止于目前的这个 location 了,在完成重写 URI 之后就开始执行该 location 下面的其他操作了。

 

示例3:参数后面 ? 的作用测试,修改配置,我们用到另外一个变量 args

server {
    listen      8083;
    server_name localhost;
    rewrite_log  on;
    error_log    /data/logs/nginx/rewrite-error.log;
    access_log   /data/logs/nginx/rewrite-access.log mylog;

    location ^~ /hello {
        rewrite ^/(.*)  /world/?from=$1 break;
        echo "URI: $uri";
        echo "ARG: $args";
    }
}

访问结果如下:

技术分享图片

修改 rewrite 配置,添加 ?:rewrite ^/(.*)  /world/?from=$1? break; 重载访问:

技术分享图片

可以发现:

如果 replacement 中包含参数,那默认旧 URI 中的请求参数也会拼接到 replacement 后面作为新的 URI,如果不希望这样,只需在 replacement 的后面加上 ?。

 

 

指令:set

 

我们一直都在说,nginx 为我们提供了很多的内部变量,但是有些时候这些变量并不能满足我们的需求,我们需要其它的一些自定变量来协助我们完成一个比较复杂的需求。set 就是这样一个指定,用来定义属于我们自己的变量,它的基本语法如下:

set $variable value;

举个例子,我们在 vhosts 下新增配置:set-demo.conf

server {
    listen      8084;
    server_name localhost;
    set $STEP 1;

    location / {
        set $STEP $STEP-2;
        echo $STEP;
    }
}

重载配置,访问测试:

技术分享图片

 

 

指令:if 和 try_files

 

在 nginx 中,我们也可以像在其它编程语言一样添加逻辑判断,其中就有 if 和 try_files,if 一般在旧版中使用,但是新版中并不影响。语法格式:

if (判断条件) {...}

 

判断只能在 server 和 location 中使用。

1. 当判断条件只是一个变量的时候,只有该变量的值为空或者 0 的时候才为 false。

2. 变量可以通过 = 或者 != 来判断,如:$var = 123。

3. 判断条件里面也可以是一个正则匹配,如:$var ~ regex。

4. 其它的一些文件,目录校验符号,如:-d / -f / -e / -x。

 

文件校验符如下:

符号作用
-f 检验文件是否存在,可以取反:!-f
-d 检验目录是否存在
-e 检验文件/目录/链接文件是否存在
-x 检验文件是否为可执行文件

 

举个例子测试,在 vhosts 目录下创建:if-demo.conf

server {
    listen      8085;
    server_name localhost;
    set $VAR 1;
    set $STEP $VAR;
    
    location / {
        if ($VAR) {
            echo "URL 0: VARIABLE TEST 0";
            set $STEP $STEP-0;
        }
    
        if ($VAR = 1) {
            echo "URL 1: VARIABLE TEST 1";
            set $STEP $STEP-1;
        }
        
        if ($http_user_agent ~* Mozilla) {
            echo "URL 2: BROWSER";
            set $STEP $STEP-2;
        }
        
        if ($http_user_agent ~ curl) {
            echo "URL 3: COMMAND";
            set $STEP $STEP-3;
        }
        
        if (-f /tmp/test.txt) {
            echo "URL 4: FILE EXIST";
            set $STEP $STEP-4;
        }
        
        if (!-f /tmp/test.txt) {
            echo "URL 5: FILE NOT EXIST";
            set $STEP $STEP-5;
            echo "STEP: $STEP";
        }
    }
}

我们定义了两个变量,VAR 和 STEP,VAR 用于测试判断,STEP 用于记录执行了哪些 if,重载访问测试:

技术分享图片

可以发现:

我们明明执行了 0 1 3 5 这 4 个 if 判断,但是真正执行的 echo 的却只有最后一个 5。

if 常常被我们用来做客户端验证,比如我们一个网站,如果是电脑打开,我们让他跳转到电脑版,手机打开跳转到手机版。

try_files 其实就是 if 的语句精简版,但是个人其实更喜欢 if 一点,所有对 try_files 感兴趣的可以详细的了解一下,我们这里举个简单的例子:

location  / {
    root      /data/www/demo;
    index     index.html index.htm;
    try_files $uri $uri/ @rewrites;
}
  
location @rewrites {
    rewrite ^(.+)$ /index.html last;
}

比如:用户访问 http://192.168.100.111/hello/world,那么 $uri 就是 /hello/world,那么 try_files 就会去指定的 root 下查找这个文件是否存在,如果存在则直接返回,如果不存在就访问第二个参数,还不存在就继续,直到最后一个参数,我们把它跳转到对应的 location 上面。在 try_files 会自行判断是文件还是目录。

虽然很方便,但是个人还是更喜欢 if 一些!

注意:

在 if 中不支持嵌套,也不支持 else,嵌套 if 可以使用多个 if 来实现它。

 

 

指令:return

 

停止一切处理,返回结果给客户端,如果返回的状态码是 444,则断开 TCP 连接,不发送任何东西。

可以使用的状态码有:204,400,402-406,408,410, 411, 413, 416 与 500-504。

如果不带状态码直接返回 URL 则被视为 302。简单示例:

在 vhosts 下面新建:return-demo.conf

server {
    listen      8086;
    server_name localhost;

    location / {
        if ($http_user_agent ~ curl) {
            return 200 COMMAND USER\n;
        }   
        if ($http_user_agent ~ Mozilla) {
            return 302 http://www.baidu.com?$args;
        }      
        return 404;
    }
}

命令行测试:

技术分享图片

浏览器访问:http://192.168.100.111:8086/hello?user=world

技术分享图片

 

 

综合示例

 

我们这里做一个结合前面的知识点一起完成的一个示例:

 

原文:https://www.cnblogs.com/Dy1an/p/11240223.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!