Nguon: Microsoft Learn · .NET 8.0

YARP Header Based Routing (Định tuyến dựa trên Header trong YARP)

Nguồn: YARP Header Based Routing

Các proxy route (tuyến đường proxy) được chỉ định trong config hoặc qua code phải bao gồm ít nhất một path (đường dẫn) hoặc host để match (khớp). Ngoài ra, một route cũng có thể chỉ định một hoặc nhiều header phải có trong yêu cầu.

Precedence (Thứ tự ưu tiên)

Thứ tự match route mặc định là:

  1. path (đường dẫn)
  2. method (phương thức)
  3. host (máy chủ)
  4. headers (tiêu đề)
  5. query parameters (tham số truy vấn)

Điều đó có nghĩa là một route chỉ định methods và không có headers sẽ match trước một route chỉ định headers và không có methods. Điều này có thể được ghi đè bằng cách đặt thuộc tính Order trên một route (xem ví dụ trong config properties).

Configuration (Cấu hình)

Headers được chỉ định trong phần Match của một proxy route.

Nếu nhiều header rules được chỉ định trên một route thì tất cả phải match để route được chọn. Logic OR phải được triển khai trong một header rule hoặc dưới dạng các route riêng biệt.

Cấu hình:

json
"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"
        }
      ]
    }
  }
}

Code:

csharp
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
                }
            }
        }
    }
};

Contract (Hợp đồng)

RouteHeader định nghĩa code contract (hợp đồng code) và được ánh xạ từ cấu hình.

Name (Tên)

Tên header cần kiểm tra trong yêu cầu. Cần có giá trị không rỗng. Trường này không phân biệt chữ hoa chữ thường theo HTTP RFCs.

Values (Các giá trị)

Danh sách các giá trị có thể để tìm kiếm. Header phải match ít nhất một trong các giá trị này theo Mode được chỉ định ngoại trừ 'NotContains'. Cần ít nhất một giá trị trừ khi Mode được đặt thành Exists hoặc NotExists.

Mode (Chế độ)

HeaderMatchMode chỉ định cách match các giá trị với request header. Mặc định là ExactHeader.

IsCaseSensitive (Phân biệt chữ hoa/thường)

Cho biết liệu việc match giá trị có nên thực hiện phân biệt chữ hoa chữ thường hay không. Mặc định là false, không phân biệt.

Examples (Ví dụ)

Các ví dụ này sử dụng cấu hình được chỉ định ở trên.

Kịch bản 1 - Exact Header Match (Khớp header chính xác)

Một yêu cầu với header sau sẽ match route1.

code
Header1: Value1

Nếu một header chứa nhiều giá trị, mỗi giá trị sẽ được match riêng biệt. Yêu cầu sau sẽ match.

code
Header1: Value1, Value2

Tương tự nếu nhiều giá trị được tách ra trên nhiều header có cùng tên.

code
Header1: Value1
Header1: Value2

Một cặp dấu ngoặc kép bao quanh có thể bị loại bỏ khỏi giá trị trước khi matching. Yêu cầu sau sẽ match.

code
Header1: "Value1"

Nhiều cặp dấu ngoặc kép sẽ không match.

code
Header1: ""Value1""

Kịch bản 2 - Multiple Values (Nhiều giá trị)

Route2 định nghĩa nhiều giá trị để tìm kiếm trong header ("1prefix", "2prefix"), bất kỳ giá trị nào cũng chấp nhận được. Nó cũng chỉ định ModeHeaderPrefix, vì vậy bất kỳ header nào bắt đầu bằng những giá trị đó đều chấp nhận được.

Bất kỳ header nào sau đây sẽ match route2.

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

Nếu một header chứa nhiều giá trị, mỗi giá trị sẽ được match riêng biệt. Yêu cầu sau sẽ match.

code
Header2: foo, 1prefix, 2prefix

Tương tự nếu nhiều giá trị được tách ra trên nhiều header có cùng tên.

code
Header2: 1prefix
Header2: 2prefix

Một cặp dấu ngoặc kép bao quanh có thể bị loại bỏ khỏi giá trị trước khi matching. Yêu cầu sau sẽ match.

code
Header2: "2prefix"

Nhiều cặp dấu ngoặc kép sẽ không match.

code
Header2: ""2prefix""

Kịch bản 3 - Exists (Tồn tại)

Route3 chỉ yêu cầu header "Header3" tồn tại với bất kỳ giá trị không rỗng nào.

Sau đây là ví dụ sẽ match route3.

code
Header3: value

Header rỗng sẽ không match.

code
Header3:

Mode này hỗ trợ các header có nhiều giá trị và nhiều header có cùng tên vì nó không xem xét nội dung header. Sau đây sẽ match.

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

Kịch bản 4 - Multiple Headers (Nhiều header)

Route4 yêu cầu cả header4header5, mỗi cái match theo Mode đã chỉ định. Các header sau sẽ match route4:

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

Những cái này sẽ không match route4 vì thiếu một trong các header yêu cầu:

code
Header4: value2
code
Header5: AnyValue

Kịch bản 5 - NotExists (Không tồn tại)

Route7 yêu cầu header "Header7" không được tồn tại.

Các header sau sẽ match route7:

code
NotHeader7: AnyValue

Các header sau sẽ không match route7 vì header "Header7" tồn tại.

code
Header7: AnyValue
code
Header7: