基于 YARP 标头的路由

config 中或通过代码指定的代理路由必须至少包含要匹配的路径或主机。 除了这些,路由还可以指定一个或多个标头,这些标头必须存在于请求中。

优先

默认路由匹配优先级顺序为

  1. 路径
  2. 方法
  3. host
  4. headers
  5. 查询参数

这意味着指定方法且没有标头的路由将在指定标头且没有方法的路由之前匹配。 可以通过在路由上设置 Order 属性来覆盖此属性(请参阅 配置属性中的示例)。

配置

标头在代理路由的 Match 节中指定。

如果在一条路由上指定了多个标头规则,那么所有规则都必须匹配才能采用该路由。 OR 逻辑必须在标头规则中或作为单独的路由实现。

配置:

"Routes": {
  "route1" : {
    "ClusterId": "cluster1",
    "Match": {
      "Path": "{**catch-all}",
      "Headers": [
        {
          "Name": "header1",
          "Values": [ "value1" ],
          "Mode": "ExactHeader"
        }
      ]
    }
  },
  "route2" : {
    "ClusterId": "cluster1",
    "Match": {
      "Path": "{**catch-all}",
      "Headers": [
        {
          "Name": "header2",
          "Values": [ "1prefix", "2prefix" ],
          "Mode": "HeaderPrefix"
        }
      ]
    }
  },
  "route3" : {
    "ClusterId": "cluster1",
    "Match": {
      "Path": "{**catch-all}",
      "Headers": [
        {
          "Name": "header3",
          "Mode": "Exists"
        }
      ]
    }
  },
  "route4" : {
    "ClusterId": "cluster1",
    "Match": {
      "Path": "{**catch-all}",
      "Headers": [
        {
          "Name": "header4",
          "Values": [ "value1", "value2" ],
          "Mode": "ExactHeader"
        },
        {
          "Name": "header5",
          "Mode": "Exists"
        }
      ]
    }
  },
  "route5" : {
    "ClusterId": "cluster1",
    "Match": {
      "Path": "{**catch-all}",
      "Headers": [
        {
          "Name": "header5",
          "Values": [ "value1", "value2" ],
          "Mode": "Contains"
        },
        {
          "Name": "header6",
          "Mode": "Exists"
        }
      ]
    }
  },
  "route6" : {
    "ClusterId": "cluster1",
    "Match": {
      "Path": "{**catch-all}",
      "Headers": [
        {
          "Name": "header6",
          "Values": [ "value1", "value2" ],
          "Mode": "NotContains"
        },
        {
          "Name": "header7",
          "Mode": "Exists"
        }
      ]
    }
  },
  "route7" : {
    "ClusterId": "cluster1",
    "Match": {
      "Path": "{**catch-all}",
      "Headers": [
        {
          "Name": "header7",
          "Mode": "NotExists"
        }
      ]
    }
  }
}

代码:

var routes = new[]
{
    new RouteConfig()
    {
        RouteId = "route1",
        ClusterId = "cluster1",
        Match = new RouteMatch
        {
            Path = "{**catch-all}",
            Headers = new[]
            {
                new RouteHeader()
                {
                    Name = "Header1",
                    Values = new[] { "value1" },
                    Mode = HeaderMatchMode.ExactHeader
                }
            }
        }
    },
    new RouteConfig()
    {
        RouteId = "route2",
        ClusterId = "cluster1",
        Match = new RouteMatch
        {
            Path = "{**catch-all}",
            Headers = new[]
            {
                new RouteHeader()
                {
                    Name = "Header2",
                    Values = new[] { "1prefix", "2prefix" },
                    Mode = HeaderMatchMode.HeaderPrefix
                }
            }
        }
    },
    new RouteConfig()
    {
        RouteId = "route3",
        ClusterId = "cluster1",
        Match = new RouteMatch
        {
            Path = "{**catch-all}",
            Headers = new[]
            {
                new RouteHeader()
                {
                    Name = "Header3",
                    Mode = HeaderMatchMode.Exists
                }
            }
        }
    },
    new RouteConfig()
    {
        RouteId = "route4",
        ClusterId = "cluster1",
        Match = new RouteMatch
        {
            Path = "{**catch-all}",
            Headers = new[]
            {
                new RouteHeader()
                {
                    Name = "Header4",
                    Values = new[] { "value1", "value2" },
                    Mode = HeaderMatchMode.ExactHeader
                },
                new RouteHeader()
                {
                    Name = "Header5",
                    Mode = HeaderMatchMode.Exists
                }
            }
        }
    },
    new RouteConfig()
    {
        RouteId = "route5",
        ClusterId = "cluster1",
        Match = new RouteMatch
        {
            Path = "{**catch-all}",
            Headers = new[]
            {
                new RouteHeader()
                {
                    Name = "Header5",
                    Values = new[] { "value1", "value2" },
                    Mode = HeaderMatchMode.Contains
                }
            }
        }
    },
    new RouteConfig()
    {
        RouteId = "route6",
        ClusterId = "cluster1",
        Match = new RouteMatch
        {
            Path = "{**catch-all}",
            Headers = new[]
            {
                new RouteHeader()
                {
                     Name = "Header6",
                    Values = new[] { "value1", "value2" },
                    Mode = HeaderMatchMode.NotContains
                }
            }
        }
    },
    new RouteConfig()
    {
        RouteId = "route7",
        ClusterId = "cluster1",
        Match = new RouteMatch
        {
            Path = "{**catch-all}",
            Headers = new[]
            {
                new RouteHeader()
                {
                    Name = "Header7",
                    Mode = HeaderMatchMode.NotExists
                }
            }
        }
    }
};

合同

RouteHeader 定义了代码契约,并从配置中映射过来。

名字

在请求上要检查的标头名称。 需要非空值。 此字段根据 HTTP RFC 不区分大小写。

价值观

要搜索的可能值的列表。 根据指定的 Mode,标头必须至少匹配其中一个值,但“NotContains”除外。 除非 Mode 设置为 ExistsNotExists,否则至少需要一个值。

模式

HeaderMatchMode 指定如何将值与请求标头匹配。 默认值为 ExactHeader

  • ExactHeader - 具有给定名称的任何标头内容必须完全匹配,并受 IsCaseSensitive的值的约束。 如果标头包含多个值(由 ,;分隔),则会在匹配之前拆分它们。 在匹配之前,也会从值中删除一对引号。
  • HeaderPrefix - 具有给定名称的任何标头都必须按前缀匹配,并受 IsCaseSensitive值的约束。 如果标头包含多个值(由 ,;分隔),则会在匹配之前拆分它们。 在匹配之前,也会从值中删除一对引号。
  • Exists - 标头必须存在并包含任何非空值。 如果有多个具有相同名称的标头,规则也将匹配。
  • Contains - 任意具有给定名称的标头必须包含其中任意一个匹配值,以 IsCaseSensitive 的值为准。
  • NotContains - 具有给定名称的标头都不能包含任何匹配值,以 IsCaseSensitive 的值为准。

IsCaseSensitive

指示值匹配是应区分大小写还是不区分大小写。 默认值为false,不区分大小写。

例子

这些示例使用上面指定的配置。

场景 1 - 完全标头匹配

具有以下标头的请求将与 route1 匹配。

Header1: Value1

如果标头包含多个值,则每个值将单独匹配。 以下请求将匹配。

Header1: Value1, Value2

如果多个值被拆分到同名的多个标头中,则情况也是如此。

Header1: Value1
Header1: Value2

在匹配之前,可以从值中删除一对闭合引号。 以下请求将匹配。

Header1: "Value1"

多对引号将匹配。

Header1: ""Value1""

方案 2 - 多个值

Route2 定义了多个值以在标头中搜索(“1prefix”、“2prefix”),任何值都是可接受的。 它还将 Mode 指定为 HeaderPrefix,因此任何以这些值开头的标头都是可接受的。

以下任何标头都将与 route2 匹配。

Header2: 1prefix
Header2: 2prefix
Header2: 1prefix-extra
Header2: 2prefix-extra

如果标头包含多个值,则每个值将单独匹配。 以下请求将匹配。

Header2: foo, 1prefix, 2prefix

如果多个值被拆分到同名的多个标头中,则情况也是如此。

Header2: 1prefix
Header2: 2prefix

在匹配之前,可以从值中删除一对闭合引号。 以下请求将匹配。

Header2: "2prefix"

多对引号将匹配。

Header2: ""2prefix""

方案 3 - 已存在

Route3 仅要求标头“Header3”存在任何非空值

下面是匹配 route3 的示例。

Header3: value

空标头将匹配。

Header3:

此模式支持具有多个值的标头和具有相同名称的多个标头,因为它不查看标头内容。 以下内容将匹配。

Header3: value1, value2
Header3: value1
Header3: value2
Header3:
Header3:

方案 4 - 多个标头

Route4 同时需要 header4header5,根据各自指定的 Mode 进行匹配。 以下标头将与 route4 匹配:

Header4: value1
Header5: AnyValue
Header4: value2
Header5: AnyValue

这些将与 route4 匹配,因为它们缺少所需的标头之一:

Header4: value2
Header5: AnyValue

方案 5 - NotExists

Route7 要求标头“Header7”不得存在

以下标头将与 route7 匹配:

NotHeader7: AnyValue

以下标头将匹配 route7,因为标头“Header7”已存在。

Header7: AnyValue
Header7: