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

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

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

Đối với 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 trình 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, đường dẫn, truy vấn và mảnh. 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, thì cả http và https đều được so khớp. Để so khớp một lược đồ cụ thể, hãy đưa lược đồ đó vào mẫu. Ngoại lệ, lược đồ http trong một mẫu khớp với cả URI yêu cầu http và https, trong khi https trong một mẫu chỉ khớp với 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ó thẩm quyền

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 URI authority (máy chủ và cổng không bắt buộc). Phần giữ chỗ hoặc ký tự đại diện không được hỗ trợ trong cơ quan quản lý 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 thêm một phân đoạn) 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.

Các mẫu tham số truy vấn sau đây được hỗ trợ:

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 Fragment

Chúng tôi hỗ trợ các loại mẫu mảnh 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ợ việc chuyển đổi tuần tự các đối số URI thành các kiểu dữ liệu gốc, 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 loại:

  • Chuyển đổi tuần tự tiêu chuẩn: Sử dụng kotlinx.serialization để chuyển đổi tuần tự thành:
    • Kiểu dữ liệu gốc (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 gốc, chuỗi hoặc enum
    • Các lớp @Serializable lồng nhau (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 lập tức cho các loại tiêu chuẩn và cấu trúc phẳng mà không cần triển khai trình chuyển đổi tuần tự tuỳ chỉnh.

Kiểu dữ liệu gốc và chuỗi

UriDeepLinkMatcher tự động giải mã các kiểu dữ liệu gốc (Boolean, Int, Long, Float, Double, Char, Byte, Short) và String:

Liệt kê

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

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 giải tuần tự thành List<T>, Set<T> hoặc Array<T>, trong đó T là một kiểu dữ liệu gốc, String hoặc enum:

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

Khi một 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 được phân tách tuỳ chỉnh (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 String và T:

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 Filter và FilterSerializer đượ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ú thích thuộc tính bằng @Serializable(with = ...):

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

Để giải tuần tự hoá các tham số truy vấn lặp lại thành một tập hợp đố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 tham 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 tá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 không khớp (trả về null để có thể thử các trình so khớp khác) và cấu hình không được hỗ trợ (gây ra ngoại lệ).

Không khớp

Sự không khớp xảy ra 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 tham số bắt buộc: Các thuộc tính khoá không rỗng không có giá trị mặc định mà 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 xảy ra trường hợp không khớp, UriDeepLinkMatcher.match sẽ trả về null, cho phép đánh giá các trình so khớp tiếp theo.

Hãy xem xét một lớp khoá và trình so khớp được định cấu hình bằng các giá trị mặc định, đố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ả tham 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 là 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 giá trị mặc định 12) 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á chứa các loại 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à tập hợp đa chiều: UriDeepLinkMatcher chỉ hỗ trợ tập hợp một chiều gồm các dữ liệu gốc, chuỗi, enum hoặc loại tuỳ chỉnh được chú thích bằng DeepLinkSerializer. Các loại Map 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 không 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.
  • Lớp lồng ghép không được làm phẳng: Không thể liên kết các lớp @Serializable lồng ghép với một phần giữ chỗ duy nhất (chẳng hạn như ?user={user}) nếu không có DeepLinkSerializer.

So sánh UriMatchResult

Các thực thể UriMatchResult được xếp hạng theo thứ tự các tiêu chí sau:

  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 đường dẫn cố định có thứ hạng cao hơn kết quả khớp phần giữ chỗ hoặc ký tự đại diện.
  3. Số lượng đối số đường dẫn: Kết quả khớp có nhiều đối số đường dẫn hơn sẽ có thứ hạng cao hơn.
  4. Sự hiện diện của đối số: Kết quả trùng khớp có chứa đối số sẽ có thứ hạng cao hơn kết quả trùng khớp không chứa đối số.
  5. Tổng số đối số: Tổng số đối số (đường dẫn, truy vấn, mảnh) là yếu tố quyết định cuối cù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 phù hợ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 miền con động hoặc định dạng đường dẫn cũ) trước khi gọi super.matchUri.
  • matchArguments: Huỷ chuyển đổi tuần tự đường dẫn, truy vấn và bản đồ đối số 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 được cung cấp. Ghi đè giá trị này để chèn giá trị động hoặc biến đổi đối số trước khi tạo thực thể 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: