nginx的变量参数于变量的使用方法详解

nginx的变量参数于变量的使用方法详解
$args                    #请求中的参数值
$query_string            #同 $args
$arg_NAME                #GET请求中NAME的值
$is_args                 #如果请求中有参数,值为"?",否则为空字符串
$uri                     #请求中的当前URI(不带请求参数,参数位于$args),可以不同于浏览器传递的$request_uri的值,它可以通过内部重定向,或者使用index指令进行修改,$uri不包含主机名,如"/foo/bar.html"。
$document_uri            #同 $uri
$document_root           #当前请求的文档根目录或别名
$host                    #优先级:HTTP请求行的主机名>"HOST"请求头字段>符合请求的服务器名.请求中的主机头字段,如果请求中的主机头不可用,则为服务器处理请求的服务器名称
$hostname                #主机名
$https                   #如果开启了SSL安全模式,值为"on",否则为空字符串。
$binary_remote_addr      #客户端地址的二进制形式,固定长度为4个字节
$body_bytes_sent         #传输给客户端的字节数,响应头不计算在内;这个变量和Apache的mod_log_config模块中的"%B"参数保持兼容
$bytes_sent              #传输给客户端的字节数
$connection              #TCP连接的序列号
$connection_requests     #TCP连接当前的请求数量
$content_length          #"Content-Length" 请求头字段
$content_type            #"Content-Type" 请求头字段
$cookie_name             #cookie名称
$limit_rate              #用于设置响应的速度限制
$msec                    #当前的Unix时间戳
$nginx_version           #nginx版本
$pid                     #工作进程的PID
$pipe                    #如果请求来自管道通信,值为"p",否则为"."
$proxy_protocol_addr     #获取代理访问服务器的客户端地址,如果是直接访问,该值为空字符串
$realpath_root           #当前请求的文档根目录或别名的真实路径,会将所有符号连接转换为真实路径
$remote_addr             #客户端地址
$remote_port             #客户端端口
$remote_user             #用于HTTP基础认证服务的用户名
$request                 #代表客户端的请求地址
$request_body            #客户端的请求主体:此变量可在location中使用,将请求主体通过proxy_pass,fastcgi_pass,uwsgi_pass和scgi_pass传递给下一级的代理服务器
$request_body_file       #将客户端请求主体保存在临时文件中。文件处理结束后,此文件需删除。如果需要之一开启此功能,需要设置client_body_in_file_only。如果将次文件传 递给后端的代理服务器,需要禁用request body,即设置proxy_pass_request_body off,fastcgi_pass_request_body off,uwsgi_pass_request_body off,or scgi_pass_request_body off
$request_completion      #如果请求成功,值为"OK",如果请求未完成或者请求不是一个范围请求的最后一部分,则为空
$request_filename        #当前连接请求的文件路径,由root或alias指令与URI请求生成
$request_length          #请求的长度 (包括请求的地址,http请求头和请求主体)
$request_method          #HTTP请求方法,通常为"GET"或"POST"
$request_time            #处理客户端请求使用的时间,单位为秒,精度毫秒; 从读入客户端的第一个字节开始,直到把最后一个字符发送给客户端后进行日志写入为止。
$request_uri             #这个变量等于包含一些客户端请求参数的原始URI,它无法修改,请查看$uri更改或重写URI,不包含主机名,例如:"/cnphp/test.php?arg=freemouse"
$scheme                  #请求使用的Web协议,"http" 或 "https"
$server_addr             #服务器端地址,需要注意的是:为了避免访问linux系统内核,应将ip地址提前设置在配置文件中
$server_name             #服务器名
$server_port             #服务器端口
$server_protocol         #服务器的HTTP版本,通常为 "HTTP/1.0" 或 "HTTP/1.1"
$status                  #HTTP响应代码
$time_iso8601            #服务器时间的ISO 8610格式
$time_local              #服务器时间(LOG Format 格式)
$cookie_NAME             #客户端请求Header头中的cookie变量,前缀"$cookie_"加上cookie名称的变量,该变量的值即为cookie名称的值
$http_NAME               #匹配任意请求头字段;变量名中的后半部分NAME可以替换成任意请求头字段,如在配置文件中需要获取http请求头:"Accept-Language",$http_accept_language即可
$http_cookie
$http_host               #请求地址,即浏览器中你输入的地址(IP或域名)
$http_referer            #url跳转来源,用来记录从那个页面链接访问过来的
$http_user_agent         #用户终端浏览器等信息
$http_x_forwarded_for
$sent_http_NAME          #可以设置任意http响应头字段;变量名中的后半部分NAME可以替换成任意响应头字段,如需要设置响应头Content-length,$sent_http_content_length即可
$sent_http_cache_control
$sent_http_connection
$sent_http_content_type
$sent_http_keep_alive
$sent_http_last_modified
$sent_http_location
$sent_http_transfer_encoding


nginx的变量参数于变量的使用方法详解

1. 设置变量

在 Nginx 配置中,变量只能存放一种类型的值,那就是字符串。Nginx中以$开头的字符串为变量对象。我们可以通过标准 ngx_http_rewrite_module 模块的 set 配置指令对变量进行赋值操作。

#nginx.conf配置文件
events
{
  worker_connections 1024;
}
http
{
  server
  {
    listen 8080;
    location /test
    {
      set $foo hello;    #使用set配置指令对变量$foo进行了赋值操作
      echo "foo is:$foo";   #使用第三方echo-nginx-module模块配置指令将$foo变量的值作为当前请求的响应体输出,echo-nginx-module需要另外安装
    }
  }
}
复制代码
#通过curl在命令行上请求/test这个接口                                                                         
> curl http://192.168.1.131:8080/test 
foo is:hello
复制代码

2.变量作用域

  • set 有两个含义: 创建变量,给变量赋值
  • 创建变量是在加载配置时候
  • 赋值变量是当请求处理上下文需要时触发
  • 直接调用一个未被创建的变量,会导致配置无法启动
  • 变量名是整个配置文件可见,但变量的值是基于每个独立请求的上下文
  • set命令可以用在 server,location,if 配置上下文中
  • 同一个上下文中,多次set同一个变量,使用配置中最后一个set值 (rewrite模块中return有额外影响)
#nginx.conf配置文件
events {

  worker_connections 1024;
}
http {

  server {

    listen 8080;
    location /foo {

      echo "foo is: $foo";   #而请求/foo接口时,我们总是得到空的 $foo值,因为用户变量未赋值就输出的话,得到的便是空字符串。
    }
    location /bar {

      set $foo 32;   #set 指令因为是在 location /bar中使用的,所以赋值操作只会在访问 /bar的请求中执行。
      echo "foo is: $foo";
    }
  }
}
复制代码
#通过curl在命令行上请求/foo这个接口   
> curl http://192.168.1.131:8080/foo
foo is: 
#通过curl在命令行上请求/bar这个接口   
> curl http://192.168.1.131:8080/bar
foo is: 32
复制代码
  • Rewrite导致NGINX内部location跳转情形下,此时请求还属于同一个请求,因此变量值是不变的
#nginx.conf配置文件
events {

  worker_connections 1024;
}
http {

  server {

    listen 8080;
    location /foo {

      set $a hello;
      rewrite ^ /bar;    #用第三方模块echo-nginx-module的echo_exec /bar也能达到相同的效果
    }
    location /bar {

      echo "a is: $a";

    }
  }
}
复制代码
#通过curl在命令行上请求/foo这个接口   
> curl http://192.168.1.131:8080/foo
a is: hello
复制代码

3. 内置变量(预定义变量)

内置变量一般用于获取请求或响应的各种信息,由NGINX各模块自动产生。 常见的内置变量例如:

  • 单个变量,例如$args,$uri, $request_uri
  • 变量群变量, 例如$arg_parameter1, $arg_parameter2 .. 类似的变量群: $cookie_*, $http_* ,$upstream_http_*,$upstream_cookie_*,$upstream_trailer_*
#nginx.conf配置文件
events
{
  worker_connections 1024;
}
http
{
  server
  {
    listen 8080;
    location /test
    {
      #单个变量
      echo "uri = $uri";
      echo "request_uri = $request_uri";
      ###变量群变量
      echo "name: $arg_name";
      echo "school: $arg_school";
    }
  }
}
复制代码
#通过curl在命令行上请求/test这个接口,并加上相关参数    
> curl http://192.168.1.131:8080/test?name=chengzw&school=TU  #加个转义&
uri = /test
request_uri = /test?name=chengzw&school=TU
name: chengzw
school: TU
复制代码
  • 问题:内置变量是否可以重新赋值? 已索引类的变量(已索引的的变量,在NGINX内部有存放该变量值的容器),诸如$uri 等此类变量一般不能被重新赋值,假如尝试对变量$uri进行赋值:
set $uri urichange;
复制代码

在Nginx启动的时候会报出如下错误

> nginx -s reload
nginx: [emerg] the duplicate "uri" variable in ......
复制代码

未索引类变量(未索引指的是当用户调用的时候去启动一个内置程序去获取当前的值,并不提前预先索引好的),诸如$args, $arg_*, $cookie_*, $http_* 等可被重新赋值

#nginx.conf配置文件
events
{
  worker_connections 1024;
}
http
{
  server
  {
    listen 8080;
    location /test
    {
      set $orig_name  $arg_name;   #先把内置变量$arg_name的值,即原始请求URL参数name的值,保存在用户变量$orig_name中
      set $args "name=tom";  #然后通过对内建变量$args进行赋值,把当前请求的参数name改为tom
      echo "original name is:$orig_name";
      echo "name is:$arg_name";  #因为对内置变量$args的修改会直接导致当前请求的URL参数发生变化,因此内建变量$arg_name自然也会发生变化
    }
  }
}
复制代码
#通过curl在命令行上请求/test这个接口,并加上相关参数  
> curl http://192.168.1.131:8080/test?name=chengzw
original name is:chengzw
name is:tom
复制代码

4. 变量与子请求

正常情况下,主子请求都应维护其自己的独立变量值,就像两个完全不同的请求 一样,父子请求之间的变量互不干扰。 在这个例子中,我们分别在/main,/foo 和 /bar 这三个 location 配置块中为同一名字的变量,$var,分别设置了不同的值并予以输出。特别地,我们在 /main 接口中,故意在调用过 /foo 和 /bar 这两个“子请求”之 后,再输出它自己的 $var 变量的值。显然/foo和/bar这两个子请求在处理过程中对变量$var各自所做的修改都丝毫没有影响到主请求/main。

#nginx.conf配置文件
events {

  worker_connections 1024;
}
http {

  server {

    listen 8080;
    location /main {

      set $var main;
      echo_location /foo;
      echo_location /bar;
      echo "main is:$var";
    }
    location /foo {

      set $var foo;
      echo "foo is:$var";
    }
    location /bar {

      set $var bar;
      echo "bar is:$var";
    }
  }
}
复制代码
#通过curl在命令行上请求/main这个接口
> curl http://192.168.1.131:8080/main
foo is:foo
bar is:bar
main is:main
复制代码

但是有可能存在父请求使用子请求的变量 值情况,比如auth_request指令。 这里我们在 /main 接口中先为 $var 变量赋值 main,然后使用 ngx_http_auth_request_module模块提供的配置指令 auth_request,发起一个到 /sub 接口的子请求,最后利用 echo 指令输出变量 $var 的值。我们看到,/sub 接口对 $var 变量值的修改影响到了主请求 /main。 所以 ngx_http_auth_request_module模块发起的子请求确实是与其父请求共享一个Nginx 变量值容器。

#nginx.conf配置文件
events {

  worker_connections 1024;
}
http {

  server {

    listen 8080;
    location /main {

      set $var main;
      auth_request /sub;
      echo "main is:$var";
    }
    location /sub {

      set $var sub;
      echo "sub is:$var";
    }
  }
}
复制代码
#通过curl在命令行上请求/main这个接口
> curl http://192.168.1.131:8080/main
main is:sub
复制代码
  • 问题:为什么自请求/sub的输出没有出现在最终的输出里呢? 因为auth_request指令会自动忽略自请求的响应体,只检查子请求的响应状态码。当状态码是2XX的时候,auth_request指令会忽略子请求而让Nginx继续处理当前的请求,否则它就会立即中断当前(主)请求的执行,返回相应的错误页。在我们的例子中,/sub子请求只是使用echo指令做了一些输出,所以隐式地返回了指示正常的200状态码。

5. 变量Mapping

Nginx的map指令可以用于定义两个Nginx变量之间的映射关系,或者说是函数关系。设置map映射,并不表示立即执行,只有当访问触发“结果变量”取值时,才会实际到map中去匹配查找。 下面这个例子中,我们用map指令定义了用户变量$foo与内置变量$args之间的映射关系。用数学函数的y=f(x)来说,$args就是自变量,$foo就是因变量,即$foo的值是由$args来决定的。

#nginx.conf配置文件
events {

  worker_connections 1024;
}
http {

  map $args $foo {

    default 0;         #default是一个特殊的匹配条件,在其他条件都不匹配时,这个条件才会匹配。当这个默认条件匹配时,就把因变量$foo的值映射为0。default这个关键字是固定的。
    debug 1;     #如果自变量$args精确匹配到了debug这个字符串,则把因变量$foo的值映射为1。
  }
  server {

    listen 8080;
    location /test {

      set $orig_foo $foo;       #先把当前$foo变量的值保存在另一个用户变量$orig_foo中
      set $args debug;         #然后强行把$args的值改为debug
      echo "original foo is:$orig_foo";
      echo "foo is:$foo";
    }
  }
}
复制代码

第一行输出显示$orig_foo的值为0,这是我们所期望的值,因为curl http://192.168.1.131:8080/test 这个请求并没有提供URL参数,于是args最初的取值就是空,再根据我们先前定义的映射规则,$foo变量在第一次被读取时的值就应该为0(即默认匹配default条件)

第二行输出显示,在强行改写$args变量的值为字符串debug之后,$foo的值仍然为0,这显然不符合映射规则因为当$args值为debug时,$foo的值应该为1,这究竟是为什么呢? 原因就是在$foo变量在第一次读取时,根据映射规则计算出的值被缓存住了,这样在当前请求的处理过程中如果再次读取这个因变量,Nginx就会直接返回缓存住的结果,而不再调用该变量的”取处理程序”再进行计算了。

#通过curl在命令行上请求/test这个接口
> curl http://192.168.1.131:8080/test
original foo is:0   
foo is:0
复制代码

为了证实这一条,我们可以在请求中直接指定URL的参数为debug。我们可以看到现在$orig_foo的值就成了1,因为变量$foo在第一次被读取时,自变量$args的值就是debug,按照这个映射规则,”取处理程序”计算的返回值便是1。而后续再读取$foo的值时,就总是得到被缓存住的1这个结果,而不论$args后来变成什么样了。(在读取变量时执行的这段特殊代码,在 Nginx 中被称为”取处理程序”(get handler);而改写变量时执行的这段特殊代码,则被称为”存处理程序”(set handler))

#通过curl在命令行上请求/test这个接口,并带上debug参数
> curl http://192.168.1.131:8080/test?debug
original foo is:1
foo is:1
复制代码
  • map缓存强制刷新参数 1.11.7版本后增加了缓存控制参数 volatile,可以在map中增加volatile参数,来强制刷新缓存
#nginx.conf配置文件
events {

  worker_connections 1024;
}
http {

  map $args $foo {

    volatile;  #增加volatile参数,来强制刷新缓存
    default 0;
    debug 1;
  }
  server {

    listen 8080;
    location /test {

      set $orig_foo $foo;
      set $args debug;
      echo "original foo is:$orig_foo";
      echo "foo is:$foo";
    }
  }
}
复制代码

可以看到由于第二次在读取$foo的时候,是重新调用该变量的”取处理程序”再进行计算,而不是使用第一次读取$foo时的值

#通过curl在命令行上请求/test这个接口
> curl http://192.168.1.131:8080/test
original foo is:0
foo is:1
复制代码

6. 变量的两种特殊值

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

举例说来:

  • 当Nginx 用户变量 $foo 创建了却未被赋值时,$foo的值便是”不合法”
#nginx.conf配置文件
events {

  worker_connections 1024;
}
http {

  error_log /var/log/nginx/error-illigal.log warn;      #指定error_log文件存放位置,这里等级要设置为warn才能看的到[warn] 26474#26474: *33 using uninitialized "foo" variable.....这个日志,如果等级高于warn是看不到的。
  server {

    listen 8080;
    location /foo {

      echo "foo is: $foo";
    }
    location /bar {

      set $foo 32;
      echo "foo is: $foo";

    }
  }
}
复制代码
#通过curl在命令行上请求/foo这个接口
> curl 192.168.1.131:8080/foo
foo is: 
复制代码

从输出上看,未初始化的 $foo 变量(”不合法”)确实和空字符串的效果等同。但是对于上面这个请求,Nginx 的错误日志文件 error.log中多出一行类似下面这样的警告:

#查看error_log文件
cat /var/log/nginx/error-illigal.log
[warn] 26474#26474: *33 using uninitialized "foo" variable.....
复制代码

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

  • 如果当前请求的 URL 参数串中并没有提及 XXX 这个参数,则 $arg_XXX 内建变量的值便是“没找到”。
#nginx.conf配置文件
events {

  worker_connections 1024;
}
http {

  error_log /var/log/nginx/error-notfound.log debug;
  server {

    listen 8080;
    location /test {

      echo "name is: $arg_name";
    }
  }
}
复制代码

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

> curl 192.168.1.131:8080/test
name is: 
复制代码
  • 无论是“不合法”也好,还是”没找到”也罢,这两种Nginx变量所拥有的特殊值,和空字符串(”)这种取值是完全不同的,比如JavaScript语言中也有专门的 undefined和null这两种特殊值,而Lua 语言中也有专门的nil值。 通过第三方模块lua-nginx-module,可以把”不合法”和”没找到”这两个特殊值与空值区分开 注:第三方模块需要另外安装,如果想省事可以直接安装OpenResty。OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。 )
#nginx.conf配置文件
events {

  worker_connections 1024;
}
http {
  server {
    listen 8080;
    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://192.168.1.132:8080/test  #"不合法"或"没找到"
name: missing
> curl http://192.168.1.132:8080/test?name=   #name没输入便是空值
name: []
> curl http://192.168.1.132:8080/test?name=chengzw  #name有输入值
name: [chengzw]

原创文章,作者:中国IPv6网,如若转载,请注明出处:https://www.ipv6s.com/soft/202212072998.html

(0)
中国IPv6网的头像中国IPv6网
上一篇 2022年11月30日 下午2:11
下一篇 2022年12月11日 上午1:33

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注