《nginx 变量漫谈》随记

agentzh nginx变量漫谈随手记

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

  1. Nginx 变量名前面有一个 $ 符号
1
2
set $a "b";
set $b "$a, $a";
  1. 有没有办法把特殊的 $字符给转义掉呢?
  • 答案是否定的, 不过幸运的是,我们可以绕过这个限制,比如通过不支持“变量插值”的模块配置指令专门构造出取值为 $ 的 Nginx 变量,然后再在 echo 中使用这个变量
1
2
3
4
5
6
7
8
9
10
11
12
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";
}
}
  1. 变量创建和加载
  • Nginx 变量的创建和赋值操作发生在全然不同的时间阶段。
  • Nginx 变量的创建只能发生在 Nginx 配置加载的时候
  • 而赋值操作则只会发生在请求实际处理的时候。
  • 这意味着不创建而直接使用变量会导致启动失败。
  • 无法在请求处理时动态地创建新的 Nginx 变量。
  • Nginx 变量一旦创建可见范围就是整个Nginx配置,跨越不同虚拟主机的 server 配置块。
  1. 内部跳转

使用第三方模块 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 ...
  1. 多个同名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 函数。