《nginx 变量漫谈》随记

agentzh nginx变量漫谈随手记

文章地址:https://openresty.org/download/agentzh-nginx-tutorials-zhcn.html

  1. Nginx 变量名前面有一个 $ 符号

    1
    2
    set $a "b";
    set $b "$a, $a";
  2. 有没有办法把特殊的 $字符给转义掉呢?

  • 答案是否定的, 不过幸运的是,我们可以绕过这个限制,比如通过不支持“变量插值”的模块配置指令专门构造出取值为 $ 的 Nginx 变量,然后再在 echo 中使用这个变量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    geo $dollar {
    default "$";
    }

    server {
    listen 8080;

    location /test {
    echo "This is a dollar sign: $dollar";
    }
    }
  1. 在“变量插值”的上下文中,还有一种特殊情况,即当引用的变量名之后紧跟着变量名的构成字符时(比如后跟字母、数字以及下划线),我们就需要使用特别的记法来消除歧义,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    server {
    listen 8080;

    location /test {
    set $first "hello ";
    echo "${first}world";
    }
    }
  2. 变量创建和加载

    • Nginx 变量的创建和赋值操作发生在全然不同的时间阶段。
    • Nginx 变量的创建只能发生在 Nginx 配置加载的时候
    • 而赋值操作则只会发生在请求实际处理的时候。
    • 这意味着不创建而直接使用变量会导致启动失败。
    • 无法在请求处理时动态地创建新的 Nginx 变量。
    • Nginx 变量一旦创建可见范围就是整个Nginx配置,跨越不同虚拟主机的 server 配置块。
  3. 内部跳转

使用第三方模块 ngx_echo 提供的 echo_exec 配置指令,发起到 location /bar 的“内部跳转”。所谓“内部跳转”,就是在处理请求的过程中,于服务器内部,从一个 location 跳转到另一个 location 的过程。不同于 301 和 302 会发生二次请求。 exec类似于linux中的exec 请求发出后不会在回到原理的location,而是直接接管。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
server {
listen 8080;

location /foo {
set $a hello;
echo_exec /bar;
}

location /bar {
echo "a = [$a]";
}
}

### 效果等同于rewrite实现

server {
listen 8080;

location /foo {
set $a hello;
rewrite ^ /bar;
}

location /bar {
echo "a = [$a]";
}
}
  1. 内置变量获取request参数
  • 取 arg 值的 $arg_XXX
  • 取 cookie 值的 $cookie_XXX
  • 取请求头的 $http_XXX
  • 取响应头的 $sent_http_XXX
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    location /test {
    echo "name: $arg_name";
    echo "class: $arg_class";
    }

    $ curl 'http://localhost:8080/test'
    name:
    class:

    $ curl 'http://localhost:8080/test?name=Tom&class=3'
    name: Tom
    class: 3

    $ curl 'http://localhost:8080/test?name=hello%20world&class=9'
    name: hello%20world
    class: 9

    ###Nginx 会在匹配参数名之前,自动把原始请求中的参数名调整为全部小写的形式

    $ curl 'http://localhost:8080/test?NAME=Marry'
    name: Marry
    class:

    $ curl 'http://localhost:8080/test?Name=Jimmy'
    name: Jimmy
    class:
  1. %XX 字符解码
  • 可以使用第三方 ngx_set_misc 模块提供的 set_unescape_uri 配置指令:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    location /test {
    set_unescape_uri $name $arg_name;
    set_unescape_uri $class $arg_class;

    echo "name: $name";
    echo "class: $class";
    }
    现在我们再看一下效果:

    $ curl 'http://localhost:8080/test?name=hello%20world&class=9'
    name: hello world
    class: 9
  1. 内建变量都是只读的,如 $uri$request_uri. 应当避免对只读变量进行赋值。

    1
    2
    3
    4
    5
    6
    7
    location /bad {
    set $uri /blah;
    echo $uri;
    }

    Nginx 在启动的时候报出错误:
    [emerg] the duplicate "uri" variable in ...
  2. 多个同名arg变量取值

  • $arg_XXX 变量在请求 URL 中有多个同名 XXX 参数时,就只会返回最先出现的那个 XXX 参数的值,而默默忽略掉其他实例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    location /test {
    content_by_lua '
    if ngx.var.arg_name == nil then
    ngx.say("name: missing")
    else
    ngx.say("name: [", ngx.var.arg_name, "]")
    end
    ';
    }

    $ curl 'http://localhost:8080/test?name=Tom&name=Jim&name=Bob'
    name: [Tom]
    要解决这些局限,可以直接在 Lua 代码中使用 ngx_lua 模块提供的 ngx.req.get_uri_args 函数。