openresty best practices

如何匹配 URI

location 语法: location 参数 uri { 配置内容 } location 块可存在的的上下文环境包括 server 块 和 location 块。server 块不用解释,但可以存在于 location 块的原因是:location 块允许嵌套。

location 参数

符号 匹配模式 优先级
= 字符串精确匹配 最高
^~ 字符串前缀匹配
~ 正则表达式匹配,大小写敏感 一般
~* 正则表达式匹配,大小写不敏感 一般

除了上面给出的四个匹配模式之外,还可以使用”/”(根目录)符号来捕获所有没有匹配成功的 location。

普通字符串精确匹配的优先级最高,如果在这一级匹配成功,则直接进入配置内容。其次使用 ^~ 的字符串前缀匹配优先级第二。正则表达式的优先级排第三,其中正则表达式的两种匹配模式( ~~\* )是平级的关系。优先级最低的是没有任何参数的普通字符串匹配。

下面结合代码范例来讲解:

location /              {echo "no match";}
location = /            {echo "home";}
location ^~ /conf1      {echo "conf1";}
location /conf2         {echo "conf2";}
location ~ ^/conf.$     {echo "conf3";}
location ~ ^/conf.*?$   {echo "conf4";}
location /conf123       {echo "conf123";}
curl localhost/              #输出:home
curl localhost/nomatch       #输出:no match
curl localhost/conf1         #输出:conf1
curl localhost/conf2         #输出:conf3
curl localhost/conf123       #输出:conf1
curl localhost/conf1234      #输出:conf1
curl localhost/conf9         #输出:conf3
curl localhost/conf987       #输出:conf4

并不是将哪个 location 规则写在前面,它就被优先匹配。例如,我们将可以捕获所有规则的”/”(根目录)写在最前面,但它并没有捕获所有的规则,而是老老实实按照优先级来办事。但是所有规则都有例外,这唯一一个例外就是当我们进行正则表达式匹配( ~~\* )的时候,谁被写在前面谁就被优先匹配。

我们来依次解释上面的输出结果:

  • /与配置 2 精确匹配,输出:home
  • /nomatch 与除了配置 1 外的结果都不匹配,所以输出 no match
  • /conf1 同时与配置 1 、配置 3 、配置 5 、配置 6 匹配,由于配置 3 是字符串前缀匹配在所有被捕获的匹配中优先级最高,所以输出 conf1。
  • /conf2 同时与配置 1 、配置 4 、配置 5 、配置 6 匹配,其中与配置 4 虽然完全一样,但由于只是普通字符串匹配(注意:普通字符串匹配和普通字符串精确匹配是两个概念,后者仅当使用 = 参数时成立),优先级比配置 5 和配置 6 的正则表达式匹配要低,由于配置 5 写在配置 6 的前面,所以被配置 5 捕获,所以输出 conf3。
  • /conf123 同时与配置 1 、配置 3 、配置 6 、配置 7 匹配,其中与配置 7 虽然完全一样,但由于配置 3 是字符串前缀匹配,在被捕获的配置中优先级最高,所以输出 conf1。
  • /conf1234 同时与配置 1 、配置 3 、配置 6 、配置 7(为什么会匹配配置 7 ,因为普通字符串匹配默认即是前缀匹配,只是不享有 ^~ 显式声明的优先级而已)匹配,但由于配置 3 是字符串前缀匹配,在被捕获的配置中优先级最高,所以输出 conf1。
  • /conf9 同时与配置 1 、配置 5 、配置 6 匹配,由于配置 5 是正则表达式匹配,且顺序先于配置 6 ,所以输出:conf3。
  • /conf987 同时与配置 1 、配置 6 匹配,尽管配置 1 在 conf 文件中的声明顺序先于配置 6 ,配置 6 的正则表达式匹配优先级高于配置 1 的普通字符串匹配,所以仍然输出:conf4

还有两个不容易引人注意的细节:

  1. 在正则表达式模式下,注意将正则表达式后面用空格与 location 块的大括号隔开。否则有可能出现这样的报错:
directive "location" has no opening "{" in */nginx.conf
  1. 还是在正则表达式模式下,正则表达式并非从字符串开头开始匹配,除非你显式地使用 ^$ 符号来限定正则表达式匹配的开头和结尾。实例如下:
location ~ hell {echo "hello world"}
location  /what {echo "what?"}

当我们使用 curl localhost/whathell 时,由于正则表达式并不从开头开始匹配,所以也会匹配到第一个配置。由于正则表达式的优先级比较高,所以当你疑惑为什么你的代码不能进入预期的配置时,可以检查一下是否是该问题。 另外,对于上面的正则表达式,将其修改成 ^hell$ 来限定字符串的开头结尾,是一种良好的编码风格。

Tags: openresty