So khớp các đường liên kết sâu URI

Để so khớp các URI phân cấp với các mẫu và trích xuất các đối số, hãy sử dụng UriDeepLinkMatcher. Thư viện này dựa vào kotlinx.serialization để chuyển đổi tuần tự các đối số đã so khớp thành các lớp khoá của bạn.

Để tạo một UriDeepLinkMatcher, hãy cung cấp một mẫu DeepLinkUri và trình chuyển đổi tuần tự cho khoá tương ứng:

Đối với các URI không phân cấp hoặc lược đồ tuỳ chỉnh (chẳng hạn như tel:), hãy xem phần Tạo các đối tượng so khớp đường liên kết sâu tuỳ chỉnh.

Các mẫu so khớp được hỗ trợ

UriDeepLinkMatcher so khớp URI dựa trên 5 thành phần: giao thức, quyền hạn, đường dẫn, truy vấn và phân đoạn. Các phần sau đây mô tả cú pháp mẫu, phần giữ chỗ đối số và quy tắc so khớp được hỗ trợ cho từng thành phần.

So khớp lược đồ

Nếu không có lược đồ nào trong mẫu URI, cả httphttps đều được so khớp. Để so khớp một lược đồ cụ thể, hãy thêm lược đồ đó vào mẫu. Tuy nhiên, giao thức http trong một mẫu sẽ khớp với cả URI yêu cầu httphttps, trong khi https trong một mẫu chỉ khớp với các yêu cầu https.

URI mẫu URI yêu cầu Trùng khớp
www.example.com https://www.example.com
www.example.com http://www.example.com
http://www.example.com http://www.example.com
http://www.example.com https://www.example.com
https://www.example.com http://www.example.com
myapp://www.example.com myapp://www.example.com

So khớp cơ quan cấp

UriDeepLinkMatcher thực hiện so khớp chính xác không phân biệt chữ hoa chữ thường trên cơ quan URI (máy chủ và cổng không bắt buộc). Không hỗ trợ phần giữ chỗ hoặc ký tự đại diện trong cơ quan và không có đối số nào được trích xuất:

URI mẫu URI yêu cầu Trùng khớp
example.com https://example.com
example.com https://EXAMPLE.COM
example.com https://sub.example.com
example.com https://www.example.com
example.com https://example.com:8080
example.com:8080 https://example.com:8080
example.com:8080 https://example.com

So khớp đường dẫn

Các mẫu đường dẫn sau đây được hỗ trợ:

URI mẫu URI yêu cầu Trùng khớp Đối số được trích xuất
www.example.com/users https://www.example.com/users Không có
www.example.com/users/{id} https://www.example.com/users/123 id: "123"
www.example.com/users/{first}-{last} https://www.example.com/users/john-doe first: "john", last: "doe"
www.example.com/users/{id}/profile https://www.example.com/users//profile id: "" (Chuỗi trống)
www.example.com/users/user_{id} https://www.example.com/users/user_123 id: "123"
www.example.com/users/{userId}/posts/{postId} https://www.example.com/users/123/posts/456 userId: "123", postId: "456"
www.example.com/users/.* https://www.example.com/users/john-doe Không có
www.example.com/users https://www.example.com/users/ ❌ (Dấu gạch chéo ở cuối tạo ra một phân đoạn bổ sung) Không áp dụng

So khớp cụm từ tìm kiếm

Thứ tự tham số truy vấn trong URI yêu cầu không cần phải khớp với thứ tự trong URI mẫu. Ngoài ra, các tham số có trong URI yêu cầu nhưng không có trong URI mẫu sẽ bị bỏ qua.

Chúng tôi hỗ trợ các mẫu tham số truy vấn sau:

URI mẫu URI yêu cầu Đối số được trích xuất
www.example.com/users?name={name} https://www.example.com/users?name=john name: "john"
www.example.com/users?name={name} https://www.example.com/users?name= name: "" (Chuỗi trống)
www.example.com/users?{rawQuery} https://www.example.com/users?anything&else rawQuery: ["anything", "else"]
www.example.com/users?type=user_{id} https://www.example.com/users?type=user_123 id: "123"
www.example.com/users?name={first}_{last} https://www.example.com/users?name=john_doe first: "john", last: "doe"
www.example.com/users?list={list} https://www.example.com/users?list=10&list=20 list: ["10", "20"]
www.example.com/users?name={name}&{other} https://www.example.com/users?name=john&tab=info name: "john", other: ["tab=info"]
www.example.com/users?type=user_.* https://www.example.com/users?type=user_admin type: "admin"

So khớp mảnh

Chúng tôi hỗ trợ các loại mẫu đoạn mã sau đây:

URI mẫu URI yêu cầu Đối số được trích xuất
www.example.com/#section1 https://www.example.com/#section1 Không có
www.example.com/#section_{id} https://www.example.com/#section_123 id: "123"
www.example.com/#section_.* https://www.example.com/#section_123 Không có

Loại dữ liệu được hỗ trợ

UriDeepLinkMatcher hỗ trợ giải tuần tự hoá các đối số URI thành các kiểu nguyên thuỷ, enum, tập hợp và đối tượng tuỳ chỉnh. Quá trình chuyển đổi tuần tự được chia thành hai danh mục:

  • Chuyển đổi tuần tự tiêu chuẩn: Sử dụng kotlinx.serialization để chuyển đổi tuần tự thành:
    • Các kiểu dữ liệu cơ bản (Boolean, Int, Long, Float, Double, Char, Byte, Short) và String
    • Liệt kê
    • Set, List hoặc Array của các kiểu dữ liệu cơ bản, chuỗi hoặc enum
    • Các lớp @Serializable lồng ghép (có các thuộc tính được làm phẳng thành các phần giữ chỗ URI riêng lẻ)
  • Chuyển đổi tuần tự tuỳ chỉnh bằng DeepLinkSerializer: Chuyển đổi giữa một String và các đối tượng tuỳ chỉnh, các loại bên ngoài (chẳng hạn như java.time.LocalDate) hoặc các tập hợp được phân tách tuỳ chỉnh.

Chuyển đổi tuần tự tiêu chuẩn

UriDeepLinkMatcher hoạt động ngay khi xuất xưởng đối với các loại tiêu chuẩn và cấu trúc được đơn giản hoá mà không yêu cầu triển khai trình chuyển đổi tuần tự tuỳ chỉnh.

Kiểu dữ liệu nguyên thuỷ và chuỗi

UriDeepLinkMatcher tự động giải mã các kiểu nguyên thuỷ (Boolean, Int, Long, Float, Double, Char, Byte, Short) và String:

Liệt kê

Các giá trị liệt kê được so khớp có phân biệt chữ hoa chữ thường với tên phần tử liệt kê:

Bộ sưu tập truy vấn lặp lại

Các tham số truy vấn có khoá lặp lại (chẳng hạn như ?id=10&id=20) sẽ tự động chuyển đổi tuần tự thành List<T>, Set<T> hoặc Array<T>, trong đó T là kiểu nguyên thuỷ, String hoặc enum:

Các lớp @Serializable lồng nhau

Khi NavKey chứa một thuộc tính có loại là một lớp @Serializable khác, UriDeepLinkMatcher sẽ làm phẳng các thuộc tính của lớp đó để mỗi thuộc tính của lớp lồng nhau ánh xạ trực tiếp đến một tham số URI riêng lẻ có cùng tên:

Để giải tuần tự hoá các đối tượng tuỳ chỉnh (chẳng hạn như Filter(key = "brand", value = "pixel")), các loại bên ngoài (chẳng hạn như java.time.LocalDate) hoặc các chuỗi tuỳ chỉnh được phân tách (chẳng hạn như các giá trị được phân tách bằng dấu phẩy), hãy mở rộng DeepLinkSerializer<T>.

DeepLinkSerializer<T> là một KSerializer<T> trừu tượng chuyển đổi giữa StringT:

abstract class DeepLinkSerializer<T : Any> : KSerializer<T> {
    abstract val serialName: String
    abstract fun deserialize(value: String): T
    abstract fun serialize(value: T): String
}

Ví dụ: hãy xem xét các định nghĩa FilterFilterSerializer được dùng trong các đoạn mã sau:

Đối tượng tuỳ chỉnh đơn lẻ

Để giải mã một đối tượng từ một chuỗi tham số URI duy nhất (chẳng hạn như ?filter=brand:google), hãy chú giải thuộc tính bằng @Serializable(with = ...):

Đối tượng tuỳ chỉnh trong các tham số truy vấn lặp lại

Để giải tuần tự các tham số truy vấn lặp lại thành một tập hợp các đối tượng tuỳ chỉnh (List<T>, Set<T> hoặc Array<T>), hãy triển khai DeepLinkSerializer<T> cho loại phần tử T và chú thích đối số loại của thuộc tính bằng @Serializable(with = ...):

Tập hợp được phân tách trong các thông số đơn lẻ

Để phân tích cú pháp các giá trị được phân tách bằng dấu phẩy hoặc dấu phân cách tuỳ chỉnh (chẳng hạn như ?ids=1,2,3) thành một tập hợp, hãy triển khai DeepLinkSerializer cho toàn bộ loại tập hợp và chú thích thuộc tính bằng @Serializable(with = ...):

Kết quả xác thực và so khớp đối số

UriDeepLinkMatcher phân biệt giữa các trường hợp không khớp (trả về null để có thể thử các đối sánh khác) và các cấu hình không được hỗ trợ (gây ra một ngoại lệ).

Thông tin không khớp

Xảy ra trường hợp không khớp khi URI yêu cầu đến không đáp ứng các yêu cầu về mẫu hoặc loại:

  • Thiếu các tham số bắt buộc: Các thuộc tính khoá không thể rỗng mà không có giá trị mặc định và các tham số URI tương ứng không có trong URI yêu cầu.
  • Lỗi phân tích cú pháp loại: Giá trị đối số được trích xuất không thể phân tích cú pháp thành loại thuộc tính dự kiến (ví dụ: "abc" cho thuộc tính Int).

Khi có sự không khớp, UriDeepLinkMatcher.match sẽ trả về null, cho phép đánh giá các đối tượng so khớp tiếp theo.

Hãy xem xét một lớp khoá và đối tượng so khớp được định cấu hình bằng các giá trị mặc định, các đối tượng lồng nhau và enum:

Bảng sau đây minh hoạ kết quả so khớp cho nhiều URI yêu cầu:

URI yêu cầu Kết quả giải mã Kết quả trận đấu
https://www.example.com/map/paris?zoom=15&style=dark&layer=SATELLITE Thành công (Đã cung cấp tất cả các thông số) UriMatchResult(MapKey("paris", 15, LayerOptions("dark", MapLayer.SATELLITE)))
https://www.example.com/map/paris?style=dark Thành công (zoom mặc định là 12, layer đến STANDARD) UriMatchResult(MapKey("paris", 12, LayerOptions("dark", MapLayer.STANDARD)))
https://www.example.com/map/paris?zoom=&style=dark Thành công (Tham số truy vấn không bắt buộc trống sử dụng 12 mặc định) UriMatchResult(MapKey("paris", 12, LayerOptions("dark", MapLayer.STANDARD)))
https://www.example.com/map?style=dark Không khớp (Thiếu tham số location bắt buộc) null
https://www.example.com/map/paris?zoom=close&style=dark Không khớp ("close" không phải là Int) null
https://www.example.com/map/paris?style=dark&layer=HYBRID Không khớp ("HYBRID" không có trong enum) null

Cấu hình không được hỗ trợ

Nếu lớp khoá của bạn chứa các kiểu dữ liệu không được hỗ trợ, thì UriDeepLinkMatcher sẽ gửi một ngoại lệ trong quá trình so khớp thay vì trả về null.

  • Bản đồ và các tập hợp đa chiều: UriDeepLinkMatcher chỉ hỗ trợ các tập hợp một chiều gồm các kiểu nguyên thuỷ, chuỗi, enum hoặc kiểu tuỳ chỉnh được chú thích bằng DeepLinkSerializer. Map các loại sẽ gửi một IllegalArgumentException, trong khi các tập hợp lồng nhau (chẳng hạn như List<List<String>>) sẽ gửi một SerializationException.
  • Bộ sưu tập đối tượng tuỳ chỉnh chưa được chú thích: Bộ sưu tập các loại tuỳ chỉnh (chẳng hạn như List<Filter>) sẽ gửi SerializationException trừ phi loại phần tử được chú thích bằng DeepLinkSerializer.
  • Các lớp lồng ghép chưa được làm phẳng: Các lớp @Serializable lồng ghép không thể được ánh xạ đến một phần giữ chỗ duy nhất (chẳng hạn như ?user={user}) mà không có DeepLinkSerializer.

So sánh UriMatchResult

Các phiên bản UriMatchResult được xếp hạng theo các tiêu chí sau đây theo thứ tự:

  1. Loại MatchResult: UriMatchResult có thứ hạng cao hơn các loại MatchResult khác.
  2. Đường dẫn chính xác: Các kết quả khớp theo nghĩa đen sẽ được xếp hạng cao hơn các kết quả khớp theo trình giữ chỗ hoặc ký tự đại diện.
  3. Số lượng đối số đường dẫn: Số lượng đối số đường dẫn càng nhiều thì thứ hạng càng cao.
  4. Sự hiện diện của các đối số: Các đối số khớp với đối số sẽ xếp hạng cao hơn những đối số không khớp.
  5. Tổng số đối số: Tổng số đối số (đường dẫn, truy vấn, phân đoạn) là tiêu chí cuối cùng để phân định thứ hạng.

Tuỳ chỉnh UriDeepLinkMatcher

UriDeepLinkMatcher là một lớp open mà bạn có thể tạo lớp con để tuỳ chỉnh hành vi so khớp URI và trích xuất đối số:

  • matchRequest: Điểm truy cập so khớp cấp cao nhất cho DeepLinkRequest đến. Ghi đè phương thức này để kiểm tra các phần bổ sung của yêu cầu hoặc áp dụng các điều kiện tiên quyết tuỳ chỉnh trước khi so khớp URI.
  • matchUri: So khớp DeepLinkUri với mẫu đã định cấu hình. Ghi đè phương thức này để chặn và chuẩn hoá các URI đến (ví dụ: viết lại các miền con động hoặc định dạng đường dẫn cũ) trước khi gọi super.matchUri.
  • matchArguments: Chuyển đổi tuần tự các đối số đường dẫn, truy vấn và mảnh đã trích xuất thành một thực thể khoá điều hướng bằng cách sử dụng serializer đã cung cấp. Ghi đè phương thức này để chèn các giá trị động hoặc biến đổi đối số trước khi khởi tạo khoá.

Ví dụ sau đây minh hoạ cách tạo lớp con UriDeepLinkMatcher để chuẩn hoá tiền tố đường dẫn URL cũ trước khi so khớp: