Mencocokkan deep link URI

Untuk mencocokkan URI hierarkis dengan pola dan mengekstrak argumen, gunakan UriDeepLinkMatcher. Library ini mengandalkan kotlinx.serialization untuk melakukan deserialisasi argumen yang cocok ke dalam class kunci Anda.

Untuk membuat UriDeepLinkMatcher, berikan pola DeepLinkUri dan serializer untuk kunci yang sesuai:

Untuk URI non-hierarkis atau skema kustom (seperti tel:), lihat Membuat pencocok deep link kustom.

Pola pencocokan yang didukung

UriDeepLinkMatcher mencocokkan URI berdasarkan lima komponennya: skema, otoritas, jalur, kueri, dan fragmen. Bagian berikut menjelaskan sintaksis pola yang didukung, placeholder argumen, dan aturan pencocokan untuk setiap komponen.

Pencocokan skema

Jika tidak ada skema dalam pola URI, http dan https akan dicocokkan. Untuk mencocokkan skema tertentu, sertakan skema tersebut dalam pola. Sebagai pengecualian, skema http dalam pola cocok dengan URI permintaan http dan https, sedangkan https dalam pola hanya cocok dengan permintaan https.

URI Pola URI Permintaan Cocok
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 ✅

Pencocokan otoritas

UriDeepLinkMatcher melakukan pencocokan persis yang tidak peka huruf besar/kecil pada otoritas URI (host dan port opsional). Placeholder atau karakter pengganti tidak didukung dalam otoritas, dan tidak ada argumen yang diekstrak:

URI Pola URI Permintaan Cocok
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 ❌

Pencocokan jalur

Pola jalur berikut didukung:

URI Pola URI Permintaan Cocok Argumen yang Diekstrak
www.example.com/users https://www.example.com/users ✅ Tidak ada
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: "" (String kosong)
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 ✅ Tidak ada
www.example.com/users https://www.example.com/users/ ❌ (Garis miring di akhir membuat segmen tambahan) T/A

Pencocokan kueri

Urutan parameter kueri dalam URI permintaan tidak harus sama dengan urutan dalam URI pola. Selain itu, parameter yang ada di URI permintaan, tetapi tidak ada di URI pola akan diabaikan.

Pola parameter kueri berikut didukung:

URI Pola URI Permintaan Argumen yang Diekstrak
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: "" (String kosong)
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"

Pencocokan fragmen

Jenis pola fragmen berikut didukung:

URI Pola URI Permintaan Argumen yang Diekstrak
www.example.com/#section1 https://www.example.com/#section1 Tidak ada
www.example.com/#section_{id} https://www.example.com/#section_123 id: "123"
www.example.com/#section_.* https://www.example.com/#section_123 Tidak ada

Jenis data yang didukung

UriDeepLinkMatcher mendukung deserialisasi argumen URI menjadi jenis primitif, enum, koleksi, dan objek kustom. Serialisasi terbagi menjadi dua kategori:

  • Serialisasi standar: Menggunakan kotlinx.serialization untuk melakukan deserialisasi menjadi:
    • Primitif (Boolean, Int, Long, Float, Double, Char, Byte, Short) dan String
    • Enum
    • Set, List, atau Array primitif, string, atau enum
    • Class @Serializable bertingkat (yang propertinya diratakan menjadi placeholder URI individual)
  • Serialisasi kustom dengan DeepLinkSerializer: Mengonversi antara satu String dan objek kustom, jenis eksternal (seperti java.time.LocalDate), atau koleksi yang dibatasi kustom.

Serialisasi standar

UriDeepLinkMatcher dapat langsung digunakan untuk jenis standar dan struktur yang di-flatten tanpa memerlukan implementasi serializer kustom.

Primitif dan string

UriDeepLinkMatcher otomatis mendekode jenis primitif (Boolean, Int, Long, Float, Double, Char, Byte, Short) dan String:

Enum

Nilai enum dicocokkan dengan nama elemen enum dengan memperhatikan huruf besar/kecil:

Koleksi kueri berulang

Parameter kueri dengan kunci berulang (seperti ?id=10&id=20) otomatis dideserialisasi menjadi List<T>, Set<T>, atau Array<T> dengan T adalah jenis primitif, String, atau enum:

Class @Serializable bertingkat

Jika NavKey berisi properti yang jenisnya adalah class @Serializable lain, UriDeepLinkMatcher akan meratakan propertinya sehingga setiap properti class bertingkat dipetakan langsung ke parameter URI individual dengan nama yang sama:

Untuk melakukan deserialisasi objek kustom (seperti Filter(key = "brand", value = "pixel")), jenis eksternal (seperti java.time.LocalDate), atau string yang dibatasi kustom (seperti CSV), perluas DeepLinkSerializer<T>.

DeepLinkSerializer<T> adalah KSerializer<T> abstrak yang mengonversi antara String dan T:

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

Misalnya, pertimbangkan definisi Filter dan FilterSerializer yang digunakan dalam cuplikan berikut:

Objek kustom tunggal

Untuk mendekode objek dari satu string parameter URI (seperti ?filter=brand:google), anotasikan properti dengan @Serializable(with = ...):

Objek kustom dalam parameter kueri berulang

Untuk melakukan deserialisasi parameter kueri berulang ke dalam kumpulan objek kustom (List<T>, Set<T>, atau Array<T>), terapkan DeepLinkSerializer<T> untuk jenis elemen T dan anotasikan argumen jenis properti dengan @Serializable(with = ...):

Koleksi yang dibatasi dalam parameter tunggal

Untuk mengurai nilai yang dipisahkan koma atau dibatasi kustom (seperti ?ids=1,2,3) menjadi koleksi, terapkan DeepLinkSerializer untuk seluruh jenis koleksi dan anotasikan properti dengan @Serializable(with = ...):

Validasi argumen dan hasil pencocokan

UriDeepLinkMatcher membedakan antara ketidakcocokan (menampilkan null sehingga pencocok lain dapat dicoba) dan konfigurasi yang tidak didukung (memunculkan pengecualian).

Ketidakcocokan

Ketidakcocokan terjadi jika URI permintaan masuk tidak memenuhi persyaratan pola atau jenis:

  • Parameter wajib tidak ada: Properti kunci yang tidak dapat di-null tanpa nilai default yang parameter URI terkaitnya tidak ada dalam URI permintaan.
  • Kegagalan penguraian jenis: Nilai argumen yang diekstrak yang tidak dapat diuraikan menjadi jenis properti yang diharapkan (misalnya, "abc" untuk properti Int).

Jika terjadi ketidakcocokan, UriDeepLinkMatcher.match akan menampilkan null, sehingga pencocok berikutnya dapat dievaluasi.

Pertimbangkan class kunci dan matcher yang dikonfigurasi dengan nilai default, objek bertingkat, dan enum:

Tabel berikut menunjukkan hasil pencocokan untuk berbagai URI permintaan:

URI Permintaan Hasil Decoding Hasil Pertandingan
https://www.example.com/map/paris?zoom=15&style=dark&layer=SATELLITE Berhasil (Semua parameter diberikan) UriMatchResult(MapKey("paris", 15, LayerOptions("dark", MapLayer.SATELLITE)))
https://www.example.com/map/paris?style=dark Berhasil (zoom ditetapkan secara default ke 12, layer ke STANDARD) UriMatchResult(MapKey("paris", 12, LayerOptions("dark", MapLayer.STANDARD)))
https://www.example.com/map/paris?zoom=&style=dark Berhasil (Parameter kueri opsional kosong menggunakan 12 default) UriMatchResult(MapKey("paris", 12, LayerOptions("dark", MapLayer.STANDARD)))
https://www.example.com/map?style=dark Tidak cocok (Parameter location yang diperlukan tidak ada) null
https://www.example.com/map/paris?zoom=close&style=dark Tidak cocok ("close" bukan Int) null
https://www.example.com/map/paris?style=dark&layer=HYBRID Tidak cocok ("HYBRID" tidak ada dalam enum) null

Konfigurasi yang tidak didukung

Jika class kunci Anda berisi jenis data yang tidak didukung, UriDeepLinkMatcher akan menampilkan pengecualian selama pencocokan, bukan menampilkan null.

  • Peta dan koleksi multi-dimensi: UriDeepLinkMatcher hanya mendukung koleksi satu dimensi dari jenis primitif, string, enum, atau jenis kustom yang dianotasi dengan DeepLinkSerializer. Jenis Map menampilkan IllegalArgumentException, sedangkan koleksi bertingkat (seperti List<List<String>>) menampilkan SerializationException.
  • Koleksi objek kustom tanpa anotasi: Koleksi jenis kustom (seperti List<Filter>) menampilkan SerializationException kecuali jika jenis elemen diberi anotasi dengan DeepLinkSerializer.
  • Class bertingkat yang tidak di-unflatten: Class @Serializable bertingkat tidak dapat dipetakan ke satu placeholder (seperti ?user={user}) tanpa DeepLinkSerializer.

Perbandingan UriMatchResult

Instance UriMatchResult diberi peringkat menggunakan kriteria berikut secara berurutan:

  1. Jenis MatchResult: UriMatchResult memiliki peringkat lebih tinggi daripada jenis MatchResult lainnya.
  2. Jalur persis: Kecocokan jalur literal memiliki peringkat lebih tinggi daripada kecocokan placeholder atau karakter pengganti.
  3. Jumlah argumen jalur: Kecocokan dengan lebih banyak argumen jalur akan memiliki peringkat lebih tinggi.
  4. Keberadaan argumen: Pencocokan yang menangkap argumen memiliki peringkat lebih tinggi daripada yang tidak.
  5. Jumlah total argumen: Jumlah total argumen (jalur, kueri, fragmen) adalah penentu akhir.

Sesuaikan UriDeepLinkMatcher

UriDeepLinkMatcher adalah class open yang dapat Anda buat subclass-nya untuk menyesuaikan perilaku pencocokan URI dan ekstraksi argumen:

  • matchRequest: Titik entri pencocokan tingkat teratas untuk DeepLinkRequest yang masuk. Ganti ini untuk memeriksa tambahan permintaan atau menerapkan prasyarat kustom sebelum pencocokan URI.
  • matchUri: Mencocokkan DeepLinkUri dengan pola yang dikonfigurasi. Ganti ini untuk menangkap dan menormalisasi URI yang masuk (misalnya, menulis ulang subdomain dinamis atau format jalur lama) sebelum memanggil super.matchUri.
  • matchArguments: Melakukan deserialisasi peta argumen jalur, kueri, dan fragmen yang diekstrak menjadi instance kunci navigasi menggunakan serializer yang disediakan. Ganti ini untuk memasukkan nilai dinamis atau mengubah argumen sebelum pembuatan instance kunci.

Contoh berikut menunjukkan subclassing UriDeepLinkMatcher untuk menormalisasi awalan jalur URL lama sebelum pencocokan: