`
风过无声
  • 浏览: 87949 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Nginx 变量漫谈(七)不合法/没找到 (转载)

 
阅读更多

转载自 http://blog.sina.com.cn/openresty

 

在 (一) 中我们提到过,Nginx 变量的值只有一种类型,那就是字符串,但是变量也有可能压根就不存在有意义的值。没有值的变量也有两种特殊的值:一种是“不合法”(invalid),另一种是“没找到”(not found)。

 

    举例说来,当 Nginx 用户变量 $foo 创建了却未被赋值时,$foo 的值便是“不合法”;而如果当前请求的 URL 参数串中并没有提及 XXX 这个参数,则 $arg_XXX 内建变量的值便是“没找到”。

 

    无论是“不合法”也好,还是“没找到”也罢,这两种 Nginx 变量所拥有的特殊值,和空字符串("")这种取值是完全不同的,比如 JavaScript 语言中也有专门的 undefined 和 null 这两种特殊值,而 Lua 语言中也有专门的 nil 值: 它们既不等同于空字符串,也不等同于数字 0,更不是布尔值 false. 其实 SQL 语言中的 NULL 也是类似的一种东西。

 

    虽然前面在 (一) 中我们看到,由 set 指令创建的变量未初始化就用在“变量插值”中时,效果等同于空字符串,但那是因为 set 指令为它创建的变量自动注册了一个“取处理程序”,将“不合法”的变量值转换为空字符串。为了验证这一点,我们再重新看一下 (一) 中讨论过的那个例子:

    location /foo {

        echo "foo = [$foo]";

    }

 

    location /bar {

        set $foo 32;

        echo "foo = [$foo]";

    }

这里为了简单起见,省略了原先写出的外围 server 配置块。在这个例子里,我们在 /bar 接口中用 set 指令隐式地创建了 $foo 变量这个名字,然后我们在 /foo 接口中不对 $foo 进行初始化就直接使用 echo 指令输出。我们当时测试 /foo 接口的结果是

    $ curl 'http://localhost:8080/foo'

    foo = []

从输出上看,未初始化的 $foo 变量确实和空字符串的效果等同。但细心的读者当时应该就已经注意到,对于上面这个请求,Nginx 的错误日志文件(一般文件名叫做 error.log)中多出一行类似下面这样的警告:

    [warn] 5765#0: *1 using uninitialized "foo" variable, ...

这一行警告是谁输出的呢?答案是 set 指令为 $foo 注册的“取处理程序”。当 /foo 接口中的 echo 指令实际执行的时候,它会对它的参数 "foo = [$foo]" 进行“变量插值”计算。于是,参数串中的 $foo 变量会被读取,而 Nginx 会首先检查其值容器里的取值,结果它看到了“不合法”这个特殊值,于是它这才决定继续调用 $foo 变量的“取处理程序”。于是 $foo 变量的“取处理程序”开始运行,它向 Nginx 的错误日志打印出上面那条警告消息,然后返回一个空字符串作为 $foo 的值,并从此缓存在 $foo 的值容器中。

 

    细心的读者会注意到刚刚描述的这个过程其实就是那些支持值缓存的内建变量的工作原理,只不过 set 指令在这里借用了这套机制来处理未正确初始化的 Nginx 变量。值得一提的是,只有“不合法”这个特殊值才会触发 Nginx 调用变量的“取处理程序”,而特殊值“没找到”却不会。

 

    上面这样的警告一般会指示出我们的 Nginx 配置中存在变量名拼写错误,抑或是在错误的场合使用了尚未初始化的变量。因为值缓存的存在,这条警告在一个请求的生命期中也不会打印多次。当然,ngx_rewrite 模块专门提供了一条 uninitialized_variable_warn 配置指令可用于禁止这条警告日志。

 

    刚才提到,内建变量 $arg_XXX 在请求 URL 参数 XXX 并不存在时会返回特殊值“找不到”,但遗憾的是在 Nginx 原生配置语言(我们估且这么称呼它)中是不能很方便地把它和空字符串区分开来的,比如:

    location /test {

        echo "name: [$arg_name]";

    }

这里我们输出 $arg_name 变量的值同时故意在请求中不提供 URL 参数 name:

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

    name: []

我们看到,输出特殊值“找不到”的效果和空字符串是相同的。因为这一回是 Nginx 的“变量插值”引擎自动把“找不到”给忽略了。

 

    那么我们究竟应当如何捕捉到“找不到”这种特殊值的踪影呢?换句话说,我们应当如何把它和空字符串给区分开来呢?显然,下面这个请求中,URL 参数 name 是有值的,而且其值应当是空字符串:

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

    name: []

但我们却无法将之和前面完全不提供 name 参数的情况给区分开。

 

    幸运的是,通过第三方模块 ngx_lua,我们可以轻松地在 Lua 代码中做到这一点。请看下面这个例子:

    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

        ';

    }

这个例子和前一个例子功能上非常接近,除了我们在 /test 接口中使用了 ngx_lua 模块的 content_by_lua 配置指令,嵌入了一小段我们自己的 Lua 代码来对 Nginx 变量 $arg_name 的特殊值进行判断。在这个例子中,当 $arg_name 的值为“没找到”(或者“不合法”)时,/foo 接口会输出 name: missing 这一行结果:

    curl 'http://localhost:8080/test'

    name: missing

因为这是我们第一次接触到 ngx_lua 模块,所以需要先简单介绍一下。ngx_lua 模块将 Lua 语言解释器(或者 LuaJIT 即时编译器)嵌入到了 Nginx 核心中,从而可以让用户在 Nginx 核心中直接运行 Lua 语言编写的程序。我们可以选择在 Nginx 不同的请求处理阶段插入我们的 Lua 代码。这些 Lua 代码既可以直接内联在 Nginx 配置文件中,也可以单独放置在外部 .lua 文件里,然后在 Nginx 配置文件中引用 .lua 文件的路径。

 

    回到上面这个例子,我们在 Lua 代码里引用 Nginx 变量都是通过 ngx.var 这个由 ngx_lua 模块提供的 Lua 接口。比如引用 Nginx 变量 $VARIABLE 时,就在 Lua 代码里写作 ngx.var.VARIABLE 就可以了。当 Nginx 变量 $arg_name 为特殊值“没找到”(或者“不合法”)时, ngx.var.arg_name 在 Lua 世界中的值就是 nil,即 Lua 语言里的“空”(不同于 Lua 空字符串)。我们在 Lua 里输出响应体内容的时候,则使用了 ngx.say 这个 Lua 函数,也是 ngx_lua 模块提供的,功能上等价于 ngx_echo 模块的 echo 配置指令。

 

    现在,如果我们提供空字符串取值的 name 参数,则输出就和刚才不相同了:

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

    name: []

在这种情况下,Nginx 变量 $arg_name 的取值便是空字符串,这既不是“没找到”,也不是“不合法”,因此在 Lua 里,ngx.var.arg_name 就返回 Lua 空字符串(""),和刚才的 Lua nil 值就完全区分开了。

 

    这种区分在有些应用场景下非常重要,比如有的 web service 接口会根据 name 这个 URL 参数是否存在来决定是否按 name 属性对数据集合进行过滤,而显然提供空字符串作为 name 参数的值,也会导致对数据集中取值为空串的记录进行筛选操作。

 

    不过,标准的 $arg_XXX 变量还是有一些局限,比如我们用下面这个请求来测试刚才那个 /test 接口:

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

    name: missing

此时,$arg_name 变量仍然读出“找不到”这个特殊值,这就明显有些违反常识。此外,$arg_XXX 变量在请求 URL 中有多个同名 XXX 参数时,就只会返回最先出现的那个 XXX 参数的值,而默默忽略掉其他实例:

    $ curl 'http://localhost:8080/test?name=Tom&name=Jim&name=Bob'

    name: [Tom]

要解决这些局限,可以直接在 Lua 代码中使用 ngx_lua 模块提供的 ngx.req.get_uri_args 函数。

 

分享到:
评论

相关推荐

    Nginx通过/etc/init.d/nginx方式启停【nginx配置文件】

    vi /etc/init.d/nginx 修改nginx后 chmod +x /etc/init.d/nginx /sbin/chkconfig nginx on sudo /sbin/chkconfig --list nginx /etc/init.d/nginx start

    nginx安装与使用.zip

    nginx安装与使用 http://blog.163.com/njut_wangjian/blog/static/1657964252013327103716818/ Nginx开发从入门到精通 http://tengine.taobao.org/book/index.html nginx官网上下载相应的安装包,--- 直接解压就...

    nginx 网站放在c://www//root

    Nginx (engine x) 是一个高性能的HTTP和反向...其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。

    开机自起nginx

    ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf #ExecReload=/usr/local/nginx/sbin/nginx -s ...

    fastDFS整合nginx主要配置文件/etc/fdfs

    用于fastDFS整合nginx,将文件拷贝到/etc/fdfs下即可。进入管理员模式,先解压文件,unzip fdfs.zip cd fdfs cp * /etc/fdfs

    Nginx Docker安装配置

    $ docker cp lw-nginx:/etc/nginx/nginx.conf /colorfulfrog/nginx/config –-将容器中的nginx.conf拷贝到宿主目录下 $ docker cp lw-nginx:/usr/share/nginx/html /colorfulfrog/nginx –-将容器中的html目录内容...

    nginx-1.10.2.zip

    windows版nginx,用于windows测试web、反向代理等。

    nginx1.24.0,包含GCC/zlib/prce依赖

    nginx1.24.0,包含GCC/zlib/prce依赖

    nginx安装文档.pdf

    https://github.com/nginx/nginx/releases 下载各个版本的nginx的地址: http://nginx.org/download/ 下载Nginx 上传nginx压缩包 通过rz命令上传⽂件:nginx-1.15.6.tar.gz 2、安装第三⽅软件 2.1、安装PCRE PCRE...

    Nginx RPM 包定制制作

    /application/nginx/sbin/nginx ss -lntup|grep nginx ps -ef|grep nginx|grep -v grep netstat -lntup|grep nginx|grep -v grep curl 127.0.0.1 mkdir -p /server/scripts cd /server/scripts/ fpm -s ...

    linux快速nginx配置

    nginx=”/usr/local/nginx/sbin/nginx” //修改成nginx执行程序的路径。 NGINX_CONF_FILE=”/usr/local/nginx/conf/nginx.conf” //修改成nginx.conf文件的路径。 保存后设置文件的执行权限 [root@localhost ~]# ...

    nginx安装教程

    sudo ./configure --sbin-path=/usr/local/nginx/nginx \ --conf-path=/usr/local/nginx/nginx.conf \ --pid-path=/usr/local/nginx/nginx.pid \ --with-http_ssl_module \ --with-pcre=/usr/local/src/pcre-8.41 \ ...

    nginx1.4.0漏洞验证

    测试用到的python文件和linux版本的nginx1.4.0源码

    nginx-1.18.0离线安装依赖包及过程

    nginx-1.18安装步骤 附件上传至服务器/opt/nginx cd /opt/nginx tar zxvf zlib-1.2.11.tar.gz cd zlib-1.2.11 ./configure make make install tar zxvf pcre-8.40.tar.gz cd pcre-8.40 ./configure make make ...

    安装 Nginx

    网上找了许久,下载了几个文档,然而都不能搭建好,这是我搭建好的,里面有配置文件:nginx.conf ...验证配置文件是否合法:/usr/local/nginx/sbin/nginx -t 命令帮助:/usr/local/nginx/sbin/nginx –h

    nginx自定义变量与内置预定义变量的使用

    总览 nginx可以使用变量简化配置与提高配置的灵活性,所有的变量值都可以通过这种方式引用: ...nginx变量的一个有趣的特性就是nginx中没一个变量都是全局可见的,而他们又不是全局变量。比如下面这个例子 location

    关于修改nginx-1.12.2/src/os/unix下的代码.png

    关于修改nginx-1.12.2/src/os/unix下的代码

    Nginx 控制 启动 脚本

    # config: /etc/nginx/nginx.conf # config: /etc/sysconfig/nginx # pidfile: /var/run/nginx.pid # description: nginx is a HTTP and reverse proxy server # ### BEGIN INIT INFO # Provides: nginx # Required-...

    Linux安装nginx/mysql/php/zabbix

    基于CentOS Linux release 7.4.1708手把手搭建环境nginx/mysql/php/zabbix

    Nginx常见错误

    Nginx常见的错误和解决方法,1、Nginx 常见启动错误 有的时候初次安装nginx的时候会报这样的错误 sbin/nginx -c conf/nginx.conf 报错内容:sbin/nginx: error while loading shared libraries: libpcre.so.1: ...

Global site tag (gtag.js) - Google Analytics